From 4730d67393c9e2786f266fe2eddc4efadaa054d8 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Sat, 3 Jun 2017 13:23:04 +0800 Subject: [PATCH] bugfix: fix nested fenced code block issue The opening ``` and closing ``` should have the same indentation. --- src/hgmarkdownhighlighter.cpp | 79 ++++++++++++++++++++++------------- src/hgmarkdownhighlighter.h | 2 + src/utils/vutils.cpp | 4 ++ src/utils/vutils.h | 4 ++ 4 files changed, 60 insertions(+), 29 deletions(-) diff --git a/src/hgmarkdownhighlighter.cpp b/src/hgmarkdownhighlighter.cpp index 11392d85..447f7482 100644 --- a/src/hgmarkdownhighlighter.cpp +++ b/src/hgmarkdownhighlighter.cpp @@ -4,6 +4,7 @@ #include #include "hgmarkdownhighlighter.h" #include "vconfigmanager.h" +#include "utils/vutils.h" extern VConfigManager vconfig; @@ -31,8 +32,9 @@ HGMarkdownHighlighter::HGMarkdownHighlighter(const QVector &s m_codeBlockStyles(codeBlockStyles), m_numOfCodeBlockHighlightsToRecv(0), parsing(0), waitInterval(waitInterval), content(NULL), capacity(0), result(NULL) { - codeBlockStartExp = QRegExp("^\\s*```(\\S*)"); - codeBlockEndExp = QRegExp("^\\s*```$"); + codeBlockStartExp = QRegExp(VUtils::c_fencedCodeBlockStartRegExp); + codeBlockEndExp = QRegExp(VUtils::c_fencedCodeBlockEndRegExp); + codeBlockFormat.setForeground(QBrush(Qt::darkYellow)); for (int index = 0; index < styles.size(); ++index) { const pmh_element_type &eleType = styles[index].type; @@ -223,34 +225,48 @@ void HGMarkdownHighlighter::initBlockHighlihgtOne(unsigned long pos, unsigned lo void HGMarkdownHighlighter::highlightCodeBlock(const QString &text) { - int nextIndex = 0; - int startIndex = 0; - if (previousBlockState() != HighlightBlockState::CodeBlock) { - startIndex = codeBlockStartExp.indexIn(text); - if (startIndex >= 0) { - nextIndex = startIndex + codeBlockStartExp.matchedLength(); + static int startLeadingSpaces = -1; + int length = 0; + int index = -1; + int preState = previousBlockState(); + int state = HighlightBlockState::Normal; + + if (preState != HighlightBlockState::CodeBlock + && preState != HighlightBlockState::CodeBlockStart) { + // Need to find a new code block start. + index = codeBlockStartExp.indexIn(text); + if (index >= 0) { + // Start a new code block. + length = text.length(); + state = HighlightBlockState::CodeBlockStart; + + // The leading spaces of code block start and end must be identical. + startLeadingSpaces = codeBlockStartExp.capturedTexts()[1].size(); } else { - nextIndex = -1; + // A normal block. + startLeadingSpaces = -1; + return; + } + } else { + // Need to find a code block end. + index = codeBlockEndExp.indexIn(text); + + // The closing ``` should have the same indentation as the open ```. + if (index >= 0 + && startLeadingSpaces == codeBlockEndExp.capturedTexts()[1].size()) { + // End of code block. + length = text.length(); + state = HighlightBlockState::CodeBlockEnd; + } else { + // Within code block. + index = 0; + length = text.length(); + state = HighlightBlockState::CodeBlock; } } - while (nextIndex >= 0) { - int endIndex = codeBlockEndExp.indexIn(text, nextIndex); - int codeBlockLength; - if (endIndex == -1) { - setCurrentBlockState(HighlightBlockState::CodeBlock); - codeBlockLength = text.length() - startIndex; - } else { - codeBlockLength = endIndex - startIndex + codeBlockEndExp.matchedLength(); - } - setFormat(startIndex, codeBlockLength, codeBlockFormat); - startIndex = codeBlockStartExp.indexIn(text, startIndex + codeBlockLength); - if (startIndex >= 0) { - nextIndex = startIndex + codeBlockStartExp.matchedLength(); - } else { - nextIndex = -1; - } - } + setCurrentBlockState(state); + setFormat(index, length, codeBlockFormat); } void HGMarkdownHighlighter::highlightLinkWithSpacesInURL(const QString &p_text) @@ -369,6 +385,7 @@ bool HGMarkdownHighlighter::updateCodeBlocks() VCodeBlock item; bool inBlock = false; + int startLeadingSpaces = -1; // Only handle complete codeblocks. QTextBlock block = document->firstBlock(); @@ -377,13 +394,14 @@ bool HGMarkdownHighlighter::updateCodeBlocks() if (inBlock) { item.m_text = item.m_text + "\n" + text; int idx = codeBlockEndExp.indexIn(text); - if (idx >= 0) { + if (idx >= 0 && codeBlockEndExp.capturedTexts()[1].size() == startLeadingSpaces) { // End block. inBlock = false; item.m_endBlock = block.blockNumber(); // See if it is a code block inside HTML comment. if (!isBlockInsideCommentRegion(block)) { + qDebug() << "add one code block in lang" << item.m_lang; codeBlocks.append(item); } } @@ -395,11 +413,14 @@ bool HGMarkdownHighlighter::updateCodeBlocks() item.m_startBlock = block.blockNumber(); item.m_startPos = block.position(); item.m_text = text; - if (codeBlockStartExp.captureCount() == 1) { - item.m_lang = codeBlockStartExp.capturedTexts()[1]; + if (codeBlockStartExp.captureCount() == 2) { + item.m_lang = codeBlockStartExp.capturedTexts()[2]; } + + startLeadingSpaces = codeBlockStartExp.capturedTexts()[1].size(); } } + block = block.next(); } diff --git a/src/hgmarkdownhighlighter.h b/src/hgmarkdownhighlighter.h index f77ce2b9..33eac9f9 100644 --- a/src/hgmarkdownhighlighter.h +++ b/src/hgmarkdownhighlighter.h @@ -28,7 +28,9 @@ enum HighlightBlockState Normal = 0, // A fenced code block. + CodeBlockStart, CodeBlock, + CodeBlockEnd, // This block is inside a HTML comment region. Comment diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index 9820bf46..f6a432d7 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -29,6 +29,10 @@ const QString VUtils::c_imageLinkRegExp = QString("\\!\\[([^\\]]*)\\]\\(([^\\)\" const QString VUtils::c_fileNameRegExp = QString("[^\\\\/:\\*\\?\"<>\\|]*"); +const QString VUtils::c_fencedCodeBlockStartRegExp = QString("^(\\s*)```([^`\\s]*)\\s*[^`]*$"); + +const QString VUtils::c_fencedCodeBlockEndRegExp = QString("^(\\s*)```$"); + VUtils::VUtils() { } diff --git a/src/utils/vutils.h b/src/utils/vutils.h index bca41ba3..5e274a52 100644 --- a/src/utils/vutils.h +++ b/src/utils/vutils.h @@ -109,6 +109,10 @@ public: // Forbidden char: \/:*?"<>| static const QString c_fileNameRegExp; + // Regular expression for fenced code block. + static const QString c_fencedCodeBlockStartRegExp; + static const QString c_fencedCodeBlockEndRegExp; + private: // static const QVector> c_availableLanguages;