Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing space between Markdown elements #2640

Merged
merged 2 commits into from Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 25 additions & 19 deletions plugins/base/src/main/kotlin/parsers/MarkdownParser.kt
Expand Up @@ -194,7 +194,9 @@ open class MarkdownParser(
private fun markdownFileHandler(node: ASTNode) =
DocTagsFromIElementFactory.getInstance(
node.type,
children = node.children.evaluateChildren()
children = node.children
.filterSpacesAndEOL()
.evaluateChildren()
)

private fun autoLinksHandler(node: ASTNode): List<DocTag> {
Expand Down Expand Up @@ -249,17 +251,23 @@ open class MarkdownParser(

private fun tableHandler(node: ASTNode) = DocTagsFromIElementFactory.getInstance(
GFMElementTypes.TABLE,
children = node.children.filterTabSeparators().evaluateChildren()
children = node.children
.filter { it.type == GFMElementTypes.ROW || it.type == GFMElementTypes.HEADER }
IgnatBeresnev marked this conversation as resolved.
Show resolved Hide resolved
.evaluateChildren()
)

private fun headerHandler(node: ASTNode) = DocTagsFromIElementFactory.getInstance(
GFMElementTypes.HEADER,
children = node.children.filterTabSeparators().evaluateChildren()
children = node.children
.filter { it.type == GFMTokenTypes.CELL }
.evaluateChildren()
)

private fun rowHandler(node: ASTNode) = DocTagsFromIElementFactory.getInstance(
GFMElementTypes.ROW,
children = node.children.filterTabSeparators().evaluateChildren()
children = node.children
.filter { it.type == GFMTokenTypes.CELL }
.evaluateChildren()
)

private fun cellHandler(node: ASTNode) = DocTagsFromIElementFactory.getInstance(
Expand Down Expand Up @@ -390,6 +398,9 @@ open class MarkdownParser(
private fun List<ASTNode>.filterTabSeparators() =
this.filterNot { it.type == GFMTokenTypes.TABLE_SEPARATOR }

private fun List<ASTNode>.filterSpacesAndEOL() =
this.filterNot { it.type == MarkdownTokenTypes.WHITE_SPACE || it.type == MarkdownTokenTypes.EOL }

private fun List<ASTNode>.evaluateChildren(keepAllFormatting: Boolean = false): List<DocTag> =
this.removeUselessTokens().swapImagesThatShouldBeLinks(keepAllFormatting).mergeLeafASTNodes().flatMap { visitNode(it, keepAllFormatting) }

Expand Down Expand Up @@ -429,9 +440,11 @@ open class MarkdownParser(
MarkdownTokenTypes.HTML_BLOCK_CONTENT
)

private fun ASTNode.isNotLeaf() = this is CompositeASTNode || this.type in notLeafNodes

private fun List<ASTNode>.isNotLeaf(index: Int): Boolean =
if (index in 0..this.lastIndex)
(this[index] is CompositeASTNode) || this[index].type in notLeafNodes
this[index].isNotLeaf()
else
false

Expand All @@ -446,33 +459,26 @@ open class MarkdownParser(
val sIndex = index
while (index < this.lastIndex) {
if (this.isNotLeaf(index + 1) || this[index + 1].startOffset != this[index].endOffset) {
mergedLeafNode(this, index, startOffset, sIndex)?.run {
children += this
}
children += mergedLeafNode(this, index, startOffset, sIndex)
break
}
index++
}
if (index == this.lastIndex) {
mergedLeafNode(this, index, startOffset, sIndex)?.run {
children += this
}
children += mergedLeafNode(this, index, startOffset, sIndex)
}
}
index++
}
return children
}

private fun mergedLeafNode(nodes: List<ASTNode>, index: Int, startOffset: Int, sIndex: Int): LeafASTNode? {
private fun mergedLeafNode(nodes: List<ASTNode>, index: Int, startOffset: Int, sIndex: Int): LeafASTNode {
val endOffset = nodes[index].endOffset
if (text.substring(startOffset, endOffset).transform().trim().isNotEmpty()) {
val type = if (nodes.subList(sIndex, index)
.any { it.type == MarkdownTokenTypes.CODE_LINE }
) MarkdownTokenTypes.CODE_LINE else MarkdownTokenTypes.TEXT
return LeafASTNode(type, startOffset, endOffset)
}
return null
val type = if (nodes.subList(sIndex, index)
.any { it.type == MarkdownTokenTypes.CODE_LINE }
) MarkdownTokenTypes.CODE_LINE else MarkdownTokenTypes.TEXT
return LeafASTNode(type, startOffset, endOffset)
}

private fun String.transform() = this
Expand Down
Expand Up @@ -9,7 +9,6 @@ import org.intellij.markdown.flavours.gfm.GFMTokenTypes
import org.jetbrains.dokka.base.translators.parseWithNormalisedSpaces
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.doc.DocTag.Companion.contentTypeParam
import java.lang.NullPointerException

object DocTagsFromIElementFactory {

Expand Down Expand Up @@ -40,7 +39,12 @@ object DocTagsFromIElementFactory {
body.orEmpty(),
children,
params
) else body?.parseWithNormalisedSpaces(renderWhiteCharactersAsSpaces = false).orEmpty()
) else {
// corner case: there are only spaces between two Markdown nodes
val containsOnlySpaces = body?.isNotEmpty() == true && body.all { it.isWhitespace() }
if (containsOnlySpaces) Text(" ", children, params)
else body?.parseWithNormalisedSpaces(renderWhiteCharactersAsSpaces = false).orEmpty()
}
MarkdownTokenTypes.HORIZONTAL_RULE -> HorizontalRule
MarkdownTokenTypes.HARD_LINE_BREAK -> Br
GFMElementTypes.STRIKETHROUGH -> Strikethrough(children, params)
Expand Down
53 changes: 51 additions & 2 deletions plugins/base/src/test/kotlin/markdown/ParserTest.kt
Expand Up @@ -2,16 +2,18 @@ package org.jetbrains.dokka.tests

import markdown.KDocTest
import org.intellij.markdown.MarkdownElementTypes
import org.jetbrains.dokka.base.parsers.MarkdownParser
import org.jetbrains.dokka.model.doc.*
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
import kotlin.test.assertTrue


class ParserTest : KDocTest() {

private fun parseMarkdownToDocNode(text: String) =
MarkdownParser( { null }, "").parseStringToDocNode(text)

@Test
fun `Simple text`() {
val kdoc = """
Expand Down Expand Up @@ -1519,5 +1521,52 @@ class ParserTest : KDocTest() {
)
executeTest(kdoc, expectedDocumentationNode)
}

@Test
fun `code with backticks`() {
val kdoc = "` `` ` ` ``` `"
val expectedDocumentationNode = DocumentationNode(
listOf(
Description(
CustomDocTag(
listOf(
P(
listOf(
CodeInline(listOf(Text("`` "))),
Text(" "),
CodeInline(listOf(Text("``` "))),
)
)
), name = MarkdownElementTypes.MARKDOWN_FILE.name
)
)
)
)
executeTest(kdoc, expectedDocumentationNode)
}

@Test
fun `should filter spaces in markdown`() {
val markdown = """
| sdsdds f,*()hhh
| dssd hf
|
| sdsdsds sdd
|
|
| eweww
|
|
|
""".trimMargin()
val actualDocumentationNode = parseMarkdownToDocNode(markdown).children
val expectedDocumentationNode = listOf(
P(listOf(Text(" sdsdds f,*()hhh dssd hf"))),
P(listOf(Text(" sdsdsds sdd"))),
P(listOf(Text(" eweww ")))
)
print(expectedDocumentationNode)
assertEquals(actualDocumentationNode, expectedDocumentationNode)
}
}

16 changes: 16 additions & 0 deletions plugins/base/src/test/kotlin/model/CommentTest.kt
Expand Up @@ -251,4 +251,20 @@ class CommentTest : AbstractModelTest("/src/main/kotlin/comment/Test.kt", "comme
}
}
}

@Test
fun `should be space between Markdown nodes`() {
inlineModelTest(
"""
|/**
| * Rotates paths by `amount` **radians** around (`x`, `y`).
| */
|val property = "test"
"""
) {
with((this / "comment" / "property").cast<DProperty>()) {
comments() equals "Rotates paths by amount radians around (x, y).\n"
}
}
}
}