From 5d164d216c06de2ae0f79ded44476b00b2de95b8 Mon Sep 17 00:00:00 2001 From: vmishenev Date: Mon, 11 Apr 2022 04:43:41 +0300 Subject: [PATCH] Support code fence of tildes --- .../base/src/main/kotlin/parsers/Parser.kt | 62 ++++++++++++++----- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/plugins/base/src/main/kotlin/parsers/Parser.kt b/plugins/base/src/main/kotlin/parsers/Parser.kt index ec55c321af..50857685ee 100644 --- a/plugins/base/src/main/kotlin/parsers/Parser.kt +++ b/plugins/base/src/main/kotlin/parsers/Parser.kt @@ -11,7 +11,7 @@ abstract class Parser { abstract fun preparse(text: String): String open fun parse(text: String): DocumentationNode = - DocumentationNode(jkdocToListOfPairs(preparse(text)).map { (tag, content) -> parseTagWithBody(tag, content) }) + DocumentationNode(extractTagsToListOfPairs(preparse(text)).map { (tag, content) -> parseTagWithBody(tag, content) }) open fun parseTagWithBody(tagName: String, content: String): TagWrapper = when (tagName) { @@ -49,18 +49,29 @@ abstract class Parser { else -> CustomTagWrapper(parseStringToDocNode(content), tagName) } - private fun jkdocToListOfPairs(javadoc: String): List> = - "description $javadoc" - .splitIgnoredInsideBackticks("\n@") + /** + * KDoc parser from Kotlin compiler relies on a comment asterisk + * So there is a mini parser here + * TODO: at least to adapt [org.jetbrains.kotlin.kdoc.lexer.KDocLexer] to parse KDoc without the asterisks and use it here + */ + private fun extractTagsToListOfPairs(text: String): List> = + "description $text" + .extractKDocSections() .map { content -> val contentWithEscapedAts = content.replace("\\@", "@") val (tag, body) = contentWithEscapedAts.split(" ", limit = 2) tag to body } - private fun CharSequence.splitIgnoredInsideBackticks(delimiter: String): List { + /** + * Ignore a doc tag inside code spans and blocks + * @see org.jetbrains.kotlin.kdoc.psi.impl.KDocSection + */ + private fun CharSequence.extractKDocSections(delimiter: String = "\n@"): List { var countOfBackticks = 0 + var countOfTildes = 0 var countOfBackticksInOpeningFence = 0 + var countOfTildesInOpeningFence = 0 var isInCode = false val result = mutableListOf() @@ -69,19 +80,38 @@ abstract class Parser { var currentOffset = 0 while (currentOffset < length) { - if (get(currentOffset) == '`') { - countOfBackticks++ - } else { - if (isInCode) { - // The closing code fence must be at least as long as the opening fence - isInCode = countOfBackticks < countOfBackticksInOpeningFence - } else { - if (countOfBackticks > 0) { - isInCode = true - countOfBackticksInOpeningFence = countOfBackticks + when (get(currentOffset)) { + '`' -> { + countOfBackticks++ + countOfTildes = 0 + } + '~' -> { + countOfTildes++ + countOfBackticks = 0 + } + else -> { + if (isInCode) { + // The closing code fence must be at least as long as the opening fence + if(countOfBackticks >= countOfBackticksInOpeningFence + || countOfTildes >= countOfTildesInOpeningFence) + isInCode = false + } else { + // as per CommonMark spec, there can be any number of backticks for a code span, not only one or three + if (countOfBackticks > 0) { + isInCode = true + countOfBackticksInOpeningFence = countOfBackticks + countOfTildesInOpeningFence = Int.MAX_VALUE + } + // tildes are only for a code block, not code span + if (countOfTildes >= 3) { + isInCode = true + countOfTildesInOpeningFence = countOfTildes + countOfBackticksInOpeningFence = Int.MAX_VALUE + } } + countOfTildes = 0 + countOfBackticks = 0 } - countOfBackticks = 0 } if (!isInCode && startsWith(delimiter, currentOffset)) { result.add(substring(rangeStart, rangeEnd))