bugfix: fix nested fenced code block issue

The opening ``` and closing ``` should have the same indentation.
This commit is contained in:
Le Tan 2017-06-03 13:23:04 +08:00
parent 85c09c296d
commit 4730d67393
4 changed files with 60 additions and 29 deletions

View File

@ -4,6 +4,7 @@
#include <algorithm> #include <algorithm>
#include "hgmarkdownhighlighter.h" #include "hgmarkdownhighlighter.h"
#include "vconfigmanager.h" #include "vconfigmanager.h"
#include "utils/vutils.h"
extern VConfigManager vconfig; extern VConfigManager vconfig;
@ -31,8 +32,9 @@ HGMarkdownHighlighter::HGMarkdownHighlighter(const QVector<HighlightingStyle> &s
m_codeBlockStyles(codeBlockStyles), m_numOfCodeBlockHighlightsToRecv(0), m_codeBlockStyles(codeBlockStyles), m_numOfCodeBlockHighlightsToRecv(0),
parsing(0), waitInterval(waitInterval), content(NULL), capacity(0), result(NULL) parsing(0), waitInterval(waitInterval), content(NULL), capacity(0), result(NULL)
{ {
codeBlockStartExp = QRegExp("^\\s*```(\\S*)"); codeBlockStartExp = QRegExp(VUtils::c_fencedCodeBlockStartRegExp);
codeBlockEndExp = QRegExp("^\\s*```$"); codeBlockEndExp = QRegExp(VUtils::c_fencedCodeBlockEndRegExp);
codeBlockFormat.setForeground(QBrush(Qt::darkYellow)); codeBlockFormat.setForeground(QBrush(Qt::darkYellow));
for (int index = 0; index < styles.size(); ++index) { for (int index = 0; index < styles.size(); ++index) {
const pmh_element_type &eleType = styles[index].type; 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) void HGMarkdownHighlighter::highlightCodeBlock(const QString &text)
{ {
int nextIndex = 0; static int startLeadingSpaces = -1;
int startIndex = 0; int length = 0;
if (previousBlockState() != HighlightBlockState::CodeBlock) { int index = -1;
startIndex = codeBlockStartExp.indexIn(text); int preState = previousBlockState();
if (startIndex >= 0) { int state = HighlightBlockState::Normal;
nextIndex = startIndex + codeBlockStartExp.matchedLength();
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 { } 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) { setCurrentBlockState(state);
int endIndex = codeBlockEndExp.indexIn(text, nextIndex); setFormat(index, length, codeBlockFormat);
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;
}
}
} }
void HGMarkdownHighlighter::highlightLinkWithSpacesInURL(const QString &p_text) void HGMarkdownHighlighter::highlightLinkWithSpacesInURL(const QString &p_text)
@ -369,6 +385,7 @@ bool HGMarkdownHighlighter::updateCodeBlocks()
VCodeBlock item; VCodeBlock item;
bool inBlock = false; bool inBlock = false;
int startLeadingSpaces = -1;
// Only handle complete codeblocks. // Only handle complete codeblocks.
QTextBlock block = document->firstBlock(); QTextBlock block = document->firstBlock();
@ -377,13 +394,14 @@ bool HGMarkdownHighlighter::updateCodeBlocks()
if (inBlock) { if (inBlock) {
item.m_text = item.m_text + "\n" + text; item.m_text = item.m_text + "\n" + text;
int idx = codeBlockEndExp.indexIn(text); int idx = codeBlockEndExp.indexIn(text);
if (idx >= 0) { if (idx >= 0 && codeBlockEndExp.capturedTexts()[1].size() == startLeadingSpaces) {
// End block. // End block.
inBlock = false; inBlock = false;
item.m_endBlock = block.blockNumber(); item.m_endBlock = block.blockNumber();
// See if it is a code block inside HTML comment. // See if it is a code block inside HTML comment.
if (!isBlockInsideCommentRegion(block)) { if (!isBlockInsideCommentRegion(block)) {
qDebug() << "add one code block in lang" << item.m_lang;
codeBlocks.append(item); codeBlocks.append(item);
} }
} }
@ -395,11 +413,14 @@ bool HGMarkdownHighlighter::updateCodeBlocks()
item.m_startBlock = block.blockNumber(); item.m_startBlock = block.blockNumber();
item.m_startPos = block.position(); item.m_startPos = block.position();
item.m_text = text; item.m_text = text;
if (codeBlockStartExp.captureCount() == 1) { if (codeBlockStartExp.captureCount() == 2) {
item.m_lang = codeBlockStartExp.capturedTexts()[1]; item.m_lang = codeBlockStartExp.capturedTexts()[2];
} }
startLeadingSpaces = codeBlockStartExp.capturedTexts()[1].size();
} }
} }
block = block.next(); block = block.next();
} }

View File

@ -28,7 +28,9 @@ enum HighlightBlockState
Normal = 0, Normal = 0,
// A fenced code block. // A fenced code block.
CodeBlockStart,
CodeBlock, CodeBlock,
CodeBlockEnd,
// This block is inside a HTML comment region. // This block is inside a HTML comment region.
Comment Comment

View File

@ -29,6 +29,10 @@ const QString VUtils::c_imageLinkRegExp = QString("\\!\\[([^\\]]*)\\]\\(([^\\)\"
const QString VUtils::c_fileNameRegExp = 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() VUtils::VUtils()
{ {
} }

View File

@ -109,6 +109,10 @@ public:
// Forbidden char: \/:*?"<>| // Forbidden char: \/:*?"<>|
static const QString c_fileNameRegExp; static const QString c_fileNameRegExp;
// Regular expression for fenced code block.
static const QString c_fencedCodeBlockStartRegExp;
static const QString c_fencedCodeBlockEndRegExp;
private: private:
// <value, name> // <value, name>
static const QVector<QPair<QString, QString>> c_availableLanguages; static const QVector<QPair<QString, QString>> c_availableLanguages;