diff --git a/src/commonMain/kotlin/org/intellij/markdown/html/GeneratingProviders.kt b/src/commonMain/kotlin/org/intellij/markdown/html/GeneratingProviders.kt index 11d5eae6..dc55f716 100644 --- a/src/commonMain/kotlin/org/intellij/markdown/html/GeneratingProviders.kt +++ b/src/commonMain/kotlin/org/intellij/markdown/html/GeneratingProviders.kt @@ -143,8 +143,6 @@ internal class HtmlBlockGeneratingProvider : GeneratingProvider { internal class CodeFenceGeneratingProvider : GeneratingProvider { override fun processNode(visitor: HtmlGenerator.HtmlGeneratingVisitor, text: String, node: ASTNode) { - val indentBefore = node.getTextInNode(text).commonPrefixWith(" ".repeat(10)).length - visitor.consumeHtml("
")
var state = 0
@@ -160,7 +158,7 @@ internal class CodeFenceGeneratingProvider : GeneratingProvider {
for (child in childrenToConsider) {
if (state == 1 && child.type in listOf(MarkdownTokenTypes.CODE_FENCE_CONTENT,
MarkdownTokenTypes.EOL)) {
- visitor.consumeHtml(HtmlGenerator.trimIndents(HtmlGenerator.leafText(text, child, false), indentBefore))
+ visitor.consumeHtml(HtmlGenerator.leafText(text, child, false))
lastChildWasContent = child.type == MarkdownTokenTypes.CODE_FENCE_CONTENT
}
if (state == 0 && child.type == MarkdownTokenTypes.FENCE_LANG) {
diff --git a/src/commonMain/kotlin/org/intellij/markdown/parser/markerblocks/impl/CodeFenceMarkerBlock.kt b/src/commonMain/kotlin/org/intellij/markdown/parser/markerblocks/impl/CodeFenceMarkerBlock.kt
index 47be7c8e..a06813ca 100644
--- a/src/commonMain/kotlin/org/intellij/markdown/parser/markerblocks/impl/CodeFenceMarkerBlock.kt
+++ b/src/commonMain/kotlin/org/intellij/markdown/parser/markerblocks/impl/CodeFenceMarkerBlock.kt
@@ -10,12 +10,14 @@ import org.intellij.markdown.parser.constraints.*
import org.intellij.markdown.parser.markerblocks.MarkerBlock
import org.intellij.markdown.parser.markerblocks.MarkerBlockImpl
import org.intellij.markdown.parser.sequentialparsers.SequentialParser
-import kotlin.math.min
import kotlin.text.Regex
-class CodeFenceMarkerBlock(myConstraints: MarkdownConstraints,
- private val productionHolder: ProductionHolder,
- private val fenceStart: String) : MarkerBlockImpl(myConstraints, productionHolder.mark()) {
+class CodeFenceMarkerBlock(
+ myConstraints: MarkdownConstraints,
+ private val productionHolder: ProductionHolder,
+ private val fenceStart: String,
+ private val fenceIndent: Int
+) : MarkerBlockImpl(myConstraints, productionHolder.mark()) {
override fun allowsSubBlocks(): Boolean = false
override fun isInterestingOffset(pos: LookaheadText.Position): Boolean = true //pos.offsetInCurrentLine == -1
@@ -54,15 +56,25 @@ class CodeFenceMarkerBlock(myConstraints: MarkdownConstraints,
realInterestingOffset = nextLineOffset
val currentLine = nextLineConstraints.eatItselfFromString(pos.currentLine)
+ // Skip characters from current constraints and advance position
+ val charactersToSkip = 1 + constraints.getCharsEaten(pos.currentLine)
+ val advancedPosition = pos.nextPosition(charactersToSkip) ?: return MarkerBlock.ProcessingResult.CANCEL
+ // Calculate actual fence indent (it can not exceed the fenceIndent)
+ val indent = advancedPosition.charsToNonWhitespace()?.coerceAtMost(fenceIndent) ?: 0
+ val startOffset = advancedPosition.offset + indent
+ val contentRange = startOffset.coerceAtMost(nextLineOffset)..nextLineOffset
if (endsThisFence(currentLine)) {
- productionHolder.addProduction(listOf(SequentialParser.Node(pos.offset + 1..pos.nextLineOrEofOffset,
- MarkdownTokenTypes.CODE_FENCE_END)))
+ productionHolder.addProduction(listOf(SequentialParser.Node(
+ startOffset..nextLineOffset,
+ MarkdownTokenTypes.CODE_FENCE_END
+ )))
scheduleProcessingResult(nextLineOffset, MarkerBlock.ProcessingResult.DEFAULT)
} else {
- val contentRange = min(pos.offset + 1 + constraints.getCharsEaten(pos.currentLine), nextLineOffset)..nextLineOffset
if (contentRange.first < contentRange.last) {
productionHolder.addProduction(listOf(SequentialParser.Node(
- contentRange, MarkdownTokenTypes.CODE_FENCE_CONTENT)))
+ contentRange,
+ MarkdownTokenTypes.CODE_FENCE_CONTENT
+ )))
}
}
diff --git a/src/commonMain/kotlin/org/intellij/markdown/parser/markerblocks/providers/CodeFenceProvider.kt b/src/commonMain/kotlin/org/intellij/markdown/parser/markerblocks/providers/CodeFenceProvider.kt
index c0cc6a7c..50617c31 100644
--- a/src/commonMain/kotlin/org/intellij/markdown/parser/markerblocks/providers/CodeFenceProvider.kt
+++ b/src/commonMain/kotlin/org/intellij/markdown/parser/markerblocks/providers/CodeFenceProvider.kt
@@ -15,25 +15,34 @@ class CodeFenceProvider : MarkerBlockProvider {
override fun createMarkerBlocks(pos: LookaheadText.Position,
productionHolder: ProductionHolder,
stateInfo: MarkerProcessor.StateInfo): List {
- val fenceAndInfo = getFenceStartAndInfo(pos, stateInfo.currentConstraints)
- if (fenceAndInfo != null) {
- createNodesForFenceStart(pos, fenceAndInfo, productionHolder)
- return listOf(CodeFenceMarkerBlock(stateInfo.currentConstraints, productionHolder, fenceAndInfo.first))
- } else {
- return emptyList()
- }
+ val fenceAndInfo = getFenceStartAndInfo(pos, stateInfo.currentConstraints) ?: return emptyList()
+ val indent = createNodesForFenceStart(pos, fenceAndInfo, productionHolder) ?: return emptyList()
+ return listOf(CodeFenceMarkerBlock(stateInfo.currentConstraints, productionHolder, fenceAndInfo.first, indent))
}
override fun interruptsParagraph(pos: LookaheadText.Position, constraints: MarkdownConstraints): Boolean {
return getFenceStartAndInfo(pos, constraints) != null
}
- private fun createNodesForFenceStart(pos: LookaheadText.Position, fenceAndInfo: Pair, productionHolder: ProductionHolder) {
+ private fun createNodesForFenceStart(
+ pos: LookaheadText.Position,
+ fenceAndInfo: Pair,
+ productionHolder: ProductionHolder
+ ): Int? {
val infoStartPosition = pos.nextLineOrEofOffset - fenceAndInfo.second.length
+ // Count the number of spaces before start element, so we can exclude them from resulting tree element
+ val indent = pos.charsToNonWhitespace() ?: 0
+ // If the current fence is indented with more than 3 spaces, it becomes a code block
+ if (indent > 3) {
+ return null
+ }
+ @Suppress("NAME_SHADOWING")
+ val pos = pos.nextPosition(indent) ?: return null
productionHolder.addProduction(listOf(SequentialParser.Node(pos.offset..infoStartPosition, MarkdownTokenTypes.CODE_FENCE_START)))
- if (fenceAndInfo.second.length > 0) {
+ if (fenceAndInfo.second.isNotEmpty()) {
productionHolder.addProduction(listOf(SequentialParser.Node(infoStartPosition..pos.nextLineOrEofOffset, MarkdownTokenTypes.FENCE_LANG)))
}
+ return indent
}
private fun getFenceStartAndInfo(pos: LookaheadText.Position, constraints: MarkdownConstraints): Pair? {
@@ -48,4 +57,4 @@ class CodeFenceProvider : MarkerBlockProvider {
companion object {
val REGEX: Regex = Regex("^ {0,3}(~~~+|```+)([^`]*)$")
}
-}
\ No newline at end of file
+}
diff --git a/src/fileBasedTest/resources/data/parser/codeFence.txt b/src/fileBasedTest/resources/data/parser/codeFence.txt
index 66484292..2f1a378f 100644
--- a/src/fileBasedTest/resources/data/parser/codeFence.txt
+++ b/src/fileBasedTest/resources/data/parser/codeFence.txt
@@ -49,15 +49,20 @@ Markdown:MARKDOWN_FILE
Markdown:EOL('\n')
Markdown:EOL('\n')
Markdown:CODE_FENCE
- Markdown:CODE_FENCE_START(' ```')
+ WHITE_SPACE(' ')
+ Markdown:CODE_FENCE_START('```')
Markdown:EOL('\n')
- Markdown:CODE_FENCE_CONTENT(' aaa')
+ WHITE_SPACE(' ')
+ Markdown:CODE_FENCE_CONTENT('aaa')
Markdown:EOL('\n')
- Markdown:CODE_FENCE_CONTENT(' aaa')
+ WHITE_SPACE(' ')
+ Markdown:CODE_FENCE_CONTENT(' aaa')
Markdown:EOL('\n')
- Markdown:CODE_FENCE_CONTENT(' aaa')
+ WHITE_SPACE(' ')
+ Markdown:CODE_FENCE_CONTENT('aaa')
Markdown:EOL('\n')
- Markdown:CODE_FENCE_END(' ```')
+ WHITE_SPACE(' ')
+ Markdown:CODE_FENCE_END('```')
Markdown:EOL('\n')
Markdown:EOL('\n')
Markdown:CODE_BLOCK
diff --git a/src/fileBasedTest/resources/data/parser/example226.txt b/src/fileBasedTest/resources/data/parser/example226.txt
index e6517903..f233d117 100644
--- a/src/fileBasedTest/resources/data/parser/example226.txt
+++ b/src/fileBasedTest/resources/data/parser/example226.txt
@@ -17,7 +17,8 @@ Markdown:MARKDOWN_FILE
WHITE_SPACE(' ')
Markdown:CODE_FENCE_CONTENT('bar')
Markdown:EOL('\n')
- Markdown:CODE_FENCE_END(' ```')
+ WHITE_SPACE(' ')
+ Markdown:CODE_FENCE_END('```')
Markdown:EOL('\n')
Markdown:LIST_ITEM
Markdown:LIST_BULLET('-')
diff --git a/src/fileBasedTest/resources/data/parser/tightLooseLists.txt b/src/fileBasedTest/resources/data/parser/tightLooseLists.txt
index 4d9d90f4..83ac62fa 100644
--- a/src/fileBasedTest/resources/data/parser/tightLooseLists.txt
+++ b/src/fileBasedTest/resources/data/parser/tightLooseLists.txt
@@ -63,7 +63,8 @@ Markdown:MARKDOWN_FILE
WHITE_SPACE(' ')
Markdown:CODE_FENCE_CONTENT(' is not loose at all')
Markdown:EOL('\n')
- Markdown:CODE_FENCE_END(' ```')
+ WHITE_SPACE(' ')
+ Markdown:CODE_FENCE_END('```')
Markdown:EOL('\n')
Markdown:LIST_ITEM
Markdown:LIST_BULLET('- ')
@@ -187,7 +188,8 @@ Markdown:MARKDOWN_FILE
WHITE_SPACE(' ')
Markdown:CODE_FENCE_CONTENT('something second')
Markdown:EOL('\n')
- Markdown:CODE_FENCE_END(' ```')
+ WHITE_SPACE(' ')
+ Markdown:CODE_FENCE_END('```')
Markdown:EOL('\n')
Markdown:LIST_ITEM
Markdown:LIST_BULLET('- ')