PegMarkdownHighlighter: use cached result to highlight before fast parse

This commit is contained in:
Le Tan 2018-08-07 21:00:13 +08:00
parent 29c0701e6b
commit 56bcc9be54
5 changed files with 252 additions and 125 deletions

View File

@ -33,6 +33,7 @@ PegHighlighterResult::PegHighlighterResult(const PegMarkdownHighlighter *p_peg,
const QSharedPointer<PegParseResult> &p_result) const QSharedPointer<PegParseResult> &p_result)
: m_timeStamp(p_result->m_timeStamp), : m_timeStamp(p_result->m_timeStamp),
m_numOfBlocks(p_result->m_numOfBlocks), m_numOfBlocks(p_result->m_numOfBlocks),
m_codeBlockHighlightReceived(false),
m_codeBlockTimeStamp(0), m_codeBlockTimeStamp(0),
m_numOfCodeBlockHighlightsToRecv(0) m_numOfCodeBlockHighlightsToRecv(0)
{ {

View File

@ -22,6 +22,11 @@ public:
return m_timeStamp == p_timeStamp; return m_timeStamp == p_timeStamp;
} }
void clear()
{
m_blocksHighlights.clear();
}
TimeStamp m_timeStamp; TimeStamp m_timeStamp;
QVector<QVector<HLUnit>> m_blocksHighlights; QVector<QVector<HLUnit>> m_blocksHighlights;
@ -56,6 +61,9 @@ public:
// Support fenced code block only. // Support fenced code block only.
QVector<QVector<HLUnitStyle> > m_codeBlocksHighlights; QVector<QVector<HLUnitStyle> > m_codeBlocksHighlights;
// Whether the code block highlight results of this result have been received.
bool m_codeBlockHighlightReceived;
// Timestamp for m_codeBlocksHighlights. // Timestamp for m_codeBlocksHighlights.
TimeStamp m_codeBlockTimeStamp; TimeStamp m_codeBlockTimeStamp;

View File

@ -21,7 +21,8 @@ PegMarkdownHighlighter::PegMarkdownHighlighter(QTextDocument *p_doc, VMdEditor *
m_timeStamp(0), m_timeStamp(0),
m_codeBlockTimeStamp(0), m_codeBlockTimeStamp(0),
m_parser(NULL), m_parser(NULL),
m_parserExts(pmh_EXT_NOTES | pmh_EXT_STRIKE | pmh_EXT_FRONTMATTER | pmh_EXT_MARK) m_parserExts(pmh_EXT_NOTES | pmh_EXT_STRIKE | pmh_EXT_FRONTMATTER | pmh_EXT_MARK),
m_notifyHighlightComplete(false)
{ {
} }
@ -66,13 +67,11 @@ void PegMarkdownHighlighter::init(const QVector<HighlightingStyle> &p_styles,
m_timer->setSingleShot(true); m_timer->setSingleShot(true);
m_timer->setInterval(p_timerInterval); m_timer->setInterval(p_timerInterval);
connect(m_timer, &QTimer::timeout, connect(m_timer, &QTimer::timeout,
this, [this]() { this, &PegMarkdownHighlighter::startParse);
startParse();
});
m_fastParseTimer = new QTimer(this); m_fastParseTimer = new QTimer(this);
m_fastParseTimer->setSingleShot(true); m_fastParseTimer->setSingleShot(true);
m_fastParseTimer->setInterval(50); m_fastParseTimer->setInterval(5);
connect(m_fastParseTimer, &QTimer::timeout, connect(m_fastParseTimer, &QTimer::timeout,
this, [this]() { this, [this]() {
QSharedPointer<PegHighlighterFastResult> result(m_fastResult); QSharedPointer<PegHighlighterFastResult> result(m_fastResult);
@ -86,21 +85,27 @@ void PegMarkdownHighlighter::init(const QVector<HighlightingStyle> &p_styles,
} }
}); });
m_rehighlightTimer = new QTimer(this); m_scrollRehighlightTimer = new QTimer(this);
m_rehighlightTimer->setSingleShot(true); m_scrollRehighlightTimer->setSingleShot(true);
m_rehighlightTimer->setInterval(5); m_scrollRehighlightTimer->setInterval(5);
connect(m_rehighlightTimer, &QTimer::timeout, connect(m_scrollRehighlightTimer, &QTimer::timeout,
this, [this]() { this, [this]() {
if (m_result->m_numOfBlocks > LARGE_BLOCK_NUMBER) { if (m_result->m_numOfBlocks > LARGE_BLOCK_NUMBER) {
rehighlightSensitiveBlocks(); rehighlightSensitiveBlocks();
} }
}); });
m_rehighlightTimer = new QTimer(this);
m_rehighlightTimer->setSingleShot(true);
m_rehighlightTimer->setInterval(10);
connect(m_rehighlightTimer, &QTimer::timeout,
this, &PegMarkdownHighlighter::rehighlightBlocks);
connect(m_doc, &QTextDocument::contentsChange, connect(m_doc, &QTextDocument::contentsChange,
this, &PegMarkdownHighlighter::handleContentsChange); this, &PegMarkdownHighlighter::handleContentsChange);
connect(m_editor->verticalScrollBar(), &QScrollBar::valueChanged, connect(m_editor->verticalScrollBar(), &QScrollBar::valueChanged,
m_rehighlightTimer, static_cast<void(QTimer::*)()>(&QTimer::start)); m_scrollRehighlightTimer, static_cast<void(QTimer::*)()>(&QTimer::start));
} }
// Just use parse results to highlight block. // Just use parse results to highlight block.
@ -112,74 +117,93 @@ void PegMarkdownHighlighter::highlightBlock(const QString &p_text)
QTextBlock block = currentBlock(); QTextBlock block = currentBlock();
int blockNum = block.blockNumber(); int blockNum = block.blockNumber();
VTextBlockData *blockData = PegMarkdownHighlighter::getBlockData(block); bool isCodeBlock = currentBlockState() == HighlightBlockState::CodeBlock;
QVector<HLUnit> *cache = NULL; VTextBlockData *blockData = VTextBlockData::blockData(block);
QVector<HLUnitStyle> *cbCache = NULL; QVector<HLUnit> *cache = &blockData->getBlockHighlightCache();
if (blockData) {
cache = &blockData->getBlockHighlightCache();
cbCache = &blockData->getCodeBlockHighlightCache();
cache->clear();
cbCache->clear();
}
bool cacheValid = true; bool cacheValid = true;
if (result->matched(m_timeStamp)) { if (result->matched(m_timeStamp)) {
if (preHighlightSingleFormatBlock(result->m_blocksHighlights, blockNum, p_text)) { if (preHighlightSingleFormatBlock(result->m_blocksHighlights,
blockNum,
p_text,
isCodeBlock)) {
cacheValid = false; cacheValid = false;
} else if (blockData->isCacheValid() && blockData->getTimeStamp() == m_timeStamp) {
// Use the cache to highlight.
highlightBlockOne(*cache);
} else {
cache->clear();
highlightBlockOne(result->m_blocksHighlights, blockNum, cache);
} }
highlightBlockOne(result->m_blocksHighlights, blockNum, cacheValid ? cache : NULL);
} else { } else {
// If fast result cover this block, we do not need to use the outdated one. // If fast result cover this block, we do not need to use the outdated one.
if (isFastParseBlock(blockNum)) { if (isFastParseBlock(blockNum)) {
preHighlightSingleFormatBlock(m_fastResult->m_blocksHighlights, blockNum, p_text); if (!preHighlightSingleFormatBlock(m_fastResult->m_blocksHighlights,
highlightBlockOne(m_fastResult->m_blocksHighlights, blockNum, NULL); blockNum,
cacheValid = false; p_text,
} else { isCodeBlock)) {
if (preHighlightSingleFormatBlock(result->m_blocksHighlights, blockNum, p_text)) { highlightBlockOne(m_fastResult->m_blocksHighlights, blockNum, NULL);
cacheValid = false;
} }
highlightBlockOne(result->m_blocksHighlights, blockNum, cacheValid ? cache : NULL); cacheValid = false;
} else {
if (preHighlightSingleFormatBlock(result->m_blocksHighlights,
blockNum,
p_text,
isCodeBlock)) {
cacheValid = false;
} else if (blockData->isCacheValid() && result->matched(blockData->getTimeStamp())) {
// Use the cache to highlight.
highlightBlockOne(*cache);
} else {
cache->clear();
highlightBlockOne(result->m_blocksHighlights, blockNum, cache);
}
} }
} }
if (currentBlockState() == HighlightBlockState::CodeBlock) { blockData->setCacheValid(cacheValid);
highlightCodeBlock(result, blockNum, p_text, cacheValid ? cbCache : NULL);
highlightCodeBlockColorColumn(p_text);
PegMarkdownHighlighter::updateBlockCodeBlockTimeStamp(block, result->m_codeBlockTimeStamp);
}
PegMarkdownHighlighter::updateBlockTimeStamp(block, result->m_timeStamp); PegMarkdownHighlighter::updateBlockTimeStamp(block, result->m_timeStamp);
if (blockData) { if (isCodeBlock) {
blockData->setCacheValid(cacheValid); QVector<HLUnitStyle> *cbCache = &blockData->getCodeBlockHighlightCache();
if (blockData->getCodeBlockTimeStamp() == result->m_codeBlockTimeStamp
|| !result->m_codeBlockHighlightReceived) {
highlightCodeBlock(*cbCache, p_text);
} else {
cbCache->clear();
highlightCodeBlock(result, blockNum, p_text, cbCache);
PegMarkdownHighlighter::updateBlockCodeBlockTimeStamp(block, result->m_codeBlockTimeStamp);
}
highlightCodeBlockColorColumn(p_text);
} }
} }
bool PegMarkdownHighlighter::preHighlightSingleFormatBlock(const QVector<QVector<HLUnit>> &p_highlights, bool PegMarkdownHighlighter::preHighlightSingleFormatBlock(const QVector<QVector<HLUnit>> &p_highlights,
int p_blockNum, int p_blockNum,
const QString &p_text) const QString &p_text,
bool p_forced)
{ {
int sz = p_text.size(); int sz = p_text.size();
if (sz == 0) { if (sz == 0) {
return false; return false;
} }
if (!m_singleFormatBlocks.contains(p_blockNum)) { if (p_highlights.size() <= p_blockNum) {
return false; return false;
} }
if (p_highlights.size() > p_blockNum) { if (!p_forced && !m_singleFormatBlocks.contains(p_blockNum)) {
const QVector<HLUnit> &units = p_highlights[p_blockNum]; return false;
if (units.size() == 1) { }
const HLUnit &unit = units[0];
if (unit.start == 0 && (int)unit.length < sz) { const QVector<HLUnit> &units = p_highlights[p_blockNum];
setFormat(unit.length, sz - unit.length, m_styles[unit.styleIndex].format); if (units.size() == 1) {
return true; const HLUnit &unit = units[0];
} if (unit.start == 0 && (int)unit.length < sz) {
setFormat(0, sz, m_styles[unit.styleIndex].format);
return true;
} }
} }
@ -200,37 +224,42 @@ bool PegMarkdownHighlighter::highlightBlockOne(const QVector<QVector<HLUnit>> &p
p_cache->append(units); p_cache->append(units);
} }
for (int i = 0; i < units.size(); ++i) { highlightBlockOne(units);
const HLUnit &unit = units[i];
if (i == 0) {
// No need to merge format.
setFormat(unit.start,
unit.length,
m_styles[unit.styleIndex].format);
} else {
QTextCharFormat newFormat = m_styles[unit.styleIndex].format;
for (int j = i - 1; j >= 0; --j) {
if (units[j].start + units[j].length <= unit.start) {
// It won't affect current unit.
continue;
} else {
// Merge the format.
QTextCharFormat tmpFormat(newFormat);
newFormat = m_styles[units[j].styleIndex].format;
// tmpFormat takes precedence.
newFormat.merge(tmpFormat);
}
}
setFormat(unit.start, unit.length, newFormat);
}
}
} }
} }
return highlighted; return highlighted;
} }
void PegMarkdownHighlighter::highlightBlockOne(const QVector<HLUnit> &p_units)
{
for (int i = 0; i < p_units.size(); ++i) {
const HLUnit &unit = p_units[i];
if (i == 0) {
// No need to merge format.
setFormat(unit.start,
unit.length,
m_styles[unit.styleIndex].format);
} else {
QTextCharFormat newFormat = m_styles[unit.styleIndex].format;
for (int j = i - 1; j >= 0; --j) {
if (p_units[j].start + p_units[j].length <= unit.start) {
// It won't affect current unit.
continue;
} else {
// Merge the format.
QTextCharFormat tmpFormat(newFormat);
newFormat = m_styles[p_units[j].styleIndex].format;
// tmpFormat takes precedence.
newFormat.merge(tmpFormat);
}
}
setFormat(unit.start, unit.length, newFormat);
}
}
}
// highlightBlock() will be called before this function. // highlightBlock() will be called before this function.
void PegMarkdownHighlighter::handleContentsChange(int p_position, int p_charsRemoved, int p_charsAdded) void PegMarkdownHighlighter::handleContentsChange(int p_position, int p_charsRemoved, int p_charsAdded)
{ {
@ -247,7 +276,11 @@ void PegMarkdownHighlighter::handleContentsChange(int p_position, int p_charsRem
} }
// We still need a timer to start a complete parse. // We still need a timer to start a complete parse.
m_timer->start(); if (m_timeStamp == 2) {
startParse();
} else {
m_timer->start();
}
} }
void PegMarkdownHighlighter::startParse() void PegMarkdownHighlighter::startParse()
@ -268,6 +301,7 @@ void PegMarkdownHighlighter::startFastParse(int p_position, int p_charsRemoved,
getFastParseBlockRange(p_position, p_charsRemoved, p_charsAdded, firstBlockNum, lastBlockNum); getFastParseBlockRange(p_position, p_charsRemoved, p_charsAdded, firstBlockNum, lastBlockNum);
if (firstBlockNum == -1) { if (firstBlockNum == -1) {
// We could not let m_fastResult NULL here. // We could not let m_fastResult NULL here.
clearFastParseResult();
return; return;
} }
@ -392,7 +426,8 @@ void PegMarkdownHighlighter::setCodeBlockHighlights(TimeStamp p_timeStamp,
exit: exit:
if (--result->m_numOfCodeBlockHighlightsToRecv <= 0) { if (--result->m_numOfCodeBlockHighlightsToRecv <= 0) {
result->m_codeBlockTimeStamp = nextCodeBlockTimeStamp(); result->m_codeBlockTimeStamp = nextCodeBlockTimeStamp();
rehighlightBlocks(); result->m_codeBlockHighlightReceived = true;
rehighlightBlocksLater();
} }
} }
@ -402,7 +437,7 @@ void PegMarkdownHighlighter::updateHighlight()
if (m_result->matched(m_timeStamp)) { if (m_result->matched(m_timeStamp)) {
// No need to parse again. Already the latest. // No need to parse again. Already the latest.
updateCodeBlocks(m_result); updateCodeBlocks(m_result);
rehighlightBlocks(); rehighlightBlocksLater();
completeHighlight(m_result); completeHighlight(m_result);
} else { } else {
startParse(); startParse();
@ -415,6 +450,8 @@ void PegMarkdownHighlighter::handleParseResult(const QSharedPointer<PegParseResu
return; return;
} }
clearFastParseResult();
m_result.reset(new PegHighlighterResult(this, p_result)); m_result.reset(new PegHighlighterResult(this, p_result));
m_result->m_codeBlockTimeStamp = nextCodeBlockTimeStamp(); m_result->m_codeBlockTimeStamp = nextCodeBlockTimeStamp();
@ -431,7 +468,12 @@ void PegMarkdownHighlighter::handleParseResult(const QSharedPointer<PegParseResu
updateCodeBlocks(m_result); updateCodeBlocks(m_result);
} }
rehighlightBlocks(); if (m_result->m_timeStamp == 2) {
m_notifyHighlightComplete = true;
rehighlightBlocks();
} else {
rehighlightBlocksLater();
}
if (matched) { if (matched) {
completeHighlight(m_result); completeHighlight(m_result);
@ -464,7 +506,11 @@ void PegMarkdownHighlighter::updateCodeBlocks(const QSharedPointer<PegHighlighte
p_result->m_codeBlocksHighlights.resize(p_result->m_numOfBlocks); p_result->m_codeBlocksHighlights.resize(p_result->m_numOfBlocks);
p_result->m_numOfCodeBlockHighlightsToRecv = cbSz; p_result->m_numOfCodeBlockHighlightsToRecv = cbSz;
} }
} else {
p_result->m_codeBlockHighlightReceived = true;
} }
} else {
p_result->m_codeBlockHighlightReceived = true;
} }
emit codeBlocksUpdated(p_result->m_timeStamp, p_result->m_codeBlocks); emit codeBlocksUpdated(p_result->m_timeStamp, p_result->m_codeBlocks);
@ -579,40 +625,61 @@ void PegMarkdownHighlighter::highlightCodeBlock(const QSharedPointer<PegHighligh
if (p_result->m_codeBlocksHighlights.size() > p_blockNum) { if (p_result->m_codeBlocksHighlights.size() > p_blockNum) {
const QVector<HLUnitStyle> &units = p_result->m_codeBlocksHighlights[p_blockNum]; const QVector<HLUnitStyle> &units = p_result->m_codeBlocksHighlights[p_blockNum];
if (!units.isEmpty()) { if (!units.isEmpty()) {
QVector<QTextCharFormat *> formats(units.size(), NULL);
if (p_cache) { if (p_cache) {
p_cache->append(units); p_cache->append(units);
} }
for (int i = 0; i < units.size(); ++i) { highlightCodeBlockOne(units);
const HLUnitStyle &unit = units[i]; }
auto it = m_codeBlockStyles.find(unit.style); }
if (it == m_codeBlockStyles.end()) { }
continue;
void PegMarkdownHighlighter::highlightCodeBlock(const QVector<HLUnitStyle> &p_units,
const QString &p_text)
{
// Brush the indentation spaces.
if (currentBlockState() == HighlightBlockState::CodeBlock) {
int spaces = VEditUtils::fetchIndentation(p_text);
if (spaces > 0) {
setFormat(0, spaces, m_codeBlockFormat);
}
}
if (!p_units.isEmpty()) {
highlightCodeBlockOne(p_units);
}
}
void PegMarkdownHighlighter::highlightCodeBlockOne(const QVector<HLUnitStyle> &p_units)
{
QVector<QTextCharFormat *> formats(p_units.size(), NULL);
for (int i = 0; i < p_units.size(); ++i) {
const HLUnitStyle &unit = p_units[i];
auto it = m_codeBlockStyles.find(unit.style);
if (it == m_codeBlockStyles.end()) {
continue;
}
formats[i] = &(*it);
QTextCharFormat newFormat = m_codeBlockFormat;
newFormat.merge(*it);
for (int j = i - 1; j >= 0; --j) {
if (p_units[j].start + p_units[j].length <= unit.start) {
// It won't affect current unit.
continue;
} else {
// Merge the format.
if (formats[j]) {
QTextCharFormat tmpFormat(newFormat);
newFormat = *(formats[j]);
// tmpFormat takes precedence.
newFormat.merge(tmpFormat);
} }
formats[i] = &(*it);
QTextCharFormat newFormat = m_codeBlockFormat;
newFormat.merge(*it);
for (int j = i - 1; j >= 0; --j) {
if (units[j].start + units[j].length <= unit.start) {
// It won't affect current unit.
continue;
} else {
// Merge the format.
if (formats[j]) {
QTextCharFormat tmpFormat(newFormat);
newFormat = *(formats[j]);
// tmpFormat takes precedence.
newFormat.merge(tmpFormat);
}
}
}
setFormat(unit.start, unit.length, newFormat);
} }
} }
setFormat(unit.start, unit.length, newFormat);
} }
} }
@ -640,14 +707,14 @@ void PegMarkdownHighlighter::highlightCodeBlockColorColumn(const QString &p_text
void PegMarkdownHighlighter::completeHighlight(QSharedPointer<PegHighlighterResult> p_result) void PegMarkdownHighlighter::completeHighlight(QSharedPointer<PegHighlighterResult> p_result)
{ {
m_notifyHighlightComplete = true;
if (isMathJaxEnabled()) { if (isMathJaxEnabled()) {
emit mathjaxBlocksUpdated(p_result->m_mathjaxBlocks); emit mathjaxBlocksUpdated(p_result->m_mathjaxBlocks);
} }
emit imageLinksUpdated(p_result->m_imageRegions); emit imageLinksUpdated(p_result->m_imageRegions);
emit headersUpdated(p_result->m_headerRegions); emit headersUpdated(p_result->m_headerRegions);
emit highlightCompleted();
} }
void PegMarkdownHighlighter::getFastParseBlockRange(int p_position, void PegMarkdownHighlighter::getFastParseBlockRange(int p_position,
@ -656,7 +723,7 @@ void PegMarkdownHighlighter::getFastParseBlockRange(int p_position,
int &p_firstBlock, int &p_firstBlock,
int &p_lastBlock) const int &p_lastBlock) const
{ {
const int maxNumOfBlocks = 100; const int maxNumOfBlocks = 50;
int charsChanged = p_charsRemoved + p_charsAdded; int charsChanged = p_charsRemoved + p_charsAdded;
QTextBlock firstBlock = m_doc->findBlock(p_position); QTextBlock firstBlock = m_doc->findBlock(p_position);
@ -674,7 +741,7 @@ void PegMarkdownHighlighter::getFastParseBlockRange(int p_position,
} }
// Look up. // Look up.
while (firstBlock.isValid() && num < maxNumOfBlocks) { while (firstBlock.isValid() && num <= maxNumOfBlocks) {
QTextBlock preBlock = firstBlock.previous(); QTextBlock preBlock = firstBlock.previous();
if (!preBlock.isValid()) { if (!preBlock.isValid()) {
break; break;
@ -705,22 +772,37 @@ goup:
} }
// Look down. // Look down.
while (lastBlock.isValid() && num < maxNumOfBlocks) { bool inCodeBlock = false;
while (lastBlock.isValid() && num <= maxNumOfBlocks) {
QTextBlock nextBlock = lastBlock.next(); QTextBlock nextBlock = lastBlock.next();
if (!nextBlock.isValid()) { if (!nextBlock.isValid()) {
break; break;
} }
// Check code block. // Check code block.
int state = lastBlock.userState(); switch (lastBlock.userState()) {
if (state == HighlightBlockState::CodeBlockStart case HighlightBlockState::CodeBlockStart:
|| state == HighlightBlockState::CodeBlock) { V_FALLTHROUGH;
case HighlightBlockState::CodeBlock:
inCodeBlock = true;
goto godown; goto godown;
case HighlightBlockState::CodeBlockEnd:
inCodeBlock = false;
break;
default:
break;
} }
// Empty block. // Empty block.
if (VEditUtils::isEmptyBlock(nextBlock)) { if (VEditUtils::isEmptyBlock(nextBlock) && !inCodeBlock) {
break; int nstate = nextBlock.userState();
if (nstate != HighlightBlockState::CodeBlockStart
&& nstate != HighlightBlockState::CodeBlock
&& nstate != HighlightBlockState::CodeBlockEnd) {
break;
}
} }
godown: godown:
@ -766,6 +848,11 @@ void PegMarkdownHighlighter::rehighlightBlocks()
} else { } else {
rehighlightSensitiveBlocks(); rehighlightSensitiveBlocks();
} }
if (m_notifyHighlightComplete) {
m_notifyHighlightComplete = false;
emit highlightCompleted();
}
} }
bool PegMarkdownHighlighter::rehighlightBlockRange(int p_first, int p_last) bool PegMarkdownHighlighter::rehighlightBlockRange(int p_first, int p_last)
@ -785,11 +872,11 @@ bool PegMarkdownHighlighter::rehighlightBlockRange(int p_first, int p_last)
bool needHL = false; bool needHL = false;
bool updateTS = false; bool updateTS = false;
VTextBlockData *data = PegMarkdownHighlighter::getBlockData(block); VTextBlockData *data = VTextBlockData::blockData(block);
if (PegMarkdownHighlighter::blockTimeStamp(block) != m_result->m_timeStamp) { if (PegMarkdownHighlighter::blockTimeStamp(block) != m_result->m_timeStamp) {
needHL = true; needHL = true;
// Try to find cache. // Try to find cache.
if (data && blockNum < hls.size()) { if (blockNum < hls.size()) {
if (data->isBlockHighlightCacheMatched(hls[blockNum])) { if (data->isBlockHighlightCacheMatched(hls[blockNum])) {
needHL = false; needHL = false;
updateTS = true; updateTS = true;
@ -802,19 +889,21 @@ bool PegMarkdownHighlighter::rehighlightBlockRange(int p_first, int p_last)
// they can be distinguished by block highlights. // they can be distinguished by block highlights.
auto it = cbStates.find(blockNum); auto it = cbStates.find(blockNum);
if (it != cbStates.end() && it.value() == HighlightBlockState::CodeBlock) { if (it != cbStates.end() && it.value() == HighlightBlockState::CodeBlock) {
if (PegMarkdownHighlighter::blockCodeBlockTimeStamp(block) != m_result->m_codeBlockTimeStamp) { if (PegMarkdownHighlighter::blockCodeBlockTimeStamp(block) != m_result->m_codeBlockTimeStamp
&& m_result->m_codeBlockHighlightReceived) {
needHL = true; needHL = true;
// Try to find cache. // Try to find cache.
if (updateTS && data && blockNum < cbHls.size()) { if (blockNum < cbHls.size()) {
if (data->isCodeBlockHighlightCacheMatched(cbHls[blockNum])) { if (data->isCodeBlockHighlightCacheMatched(cbHls[blockNum])) {
needHL = false; needHL = false;
updateTS = true;
} }
} }
} }
} }
} }
if (!needHL && data && !data->getPreviews().isEmpty()) { if (!needHL && !data->getPreviews().isEmpty()) {
needHL = true; needHL = true;
} }
@ -823,6 +912,7 @@ bool PegMarkdownHighlighter::rehighlightBlockRange(int p_first, int p_last)
rehighlightBlock(block); rehighlightBlock(block);
++nr; ++nr;
} else if (updateTS) { } else if (updateTS) {
data->setCacheValid(true);
data->setTimeStamp(m_result->m_timeStamp); data->setTimeStamp(m_result->m_timeStamp);
data->setCodeBlockTimeStamp(m_result->m_codeBlockTimeStamp); data->setCodeBlockTimeStamp(m_result->m_codeBlockTimeStamp);
} }
@ -833,3 +923,15 @@ bool PegMarkdownHighlighter::rehighlightBlockRange(int p_first, int p_last)
qDebug() << "rehighlightBlockRange" << p_first << p_last << nr; qDebug() << "rehighlightBlockRange" << p_first << p_last << nr;
return highlighted; return highlighted;
} }
void PegMarkdownHighlighter::clearFastParseResult()
{
m_fastParseBlocks.first = -1;
m_fastParseBlocks.second = -1;
m_fastResult->clear();
}
void PegMarkdownHighlighter::rehighlightBlocksLater()
{
m_rehighlightTimer->start();
}

View File

@ -93,6 +93,11 @@ private:
const QString &p_text, const QString &p_text,
QVector<HLUnitStyle> *p_cache); QVector<HLUnitStyle> *p_cache);
void highlightCodeBlock(const QVector<HLUnitStyle> &p_units,
const QString &p_text);
void highlightCodeBlockOne(const QVector<HLUnitStyle> &p_units);
// Highlight color column in code block. // Highlight color column in code block.
void highlightCodeBlockColorColumn(const QString &p_text); void highlightCodeBlockColorColumn(const QString &p_text);
@ -118,21 +123,28 @@ private:
int p_blockNum, int p_blockNum,
QVector<HLUnit> *p_cache); QVector<HLUnit> *p_cache);
// To avoid line height jitter. void highlightBlockOne(const QVector<HLUnit> &p_units);
// To avoid line height jitter and code block mess.
bool preHighlightSingleFormatBlock(const QVector<QVector<HLUnit>> &p_highlights, bool preHighlightSingleFormatBlock(const QVector<QVector<HLUnit>> &p_highlights,
int p_blockNum, int p_blockNum,
const QString &p_text); const QString &p_text,
bool p_forced);
void updateSingleFormatBlocks(const QVector<QVector<HLUnit>> &p_highlights); void updateSingleFormatBlocks(const QVector<QVector<HLUnit>> &p_highlights);
void rehighlightBlocks(); void rehighlightBlocks();
void rehighlightBlocksLater();
bool rehighlightBlockRange(int p_first, int p_last); bool rehighlightBlockRange(int p_first, int p_last);
TimeStamp nextCodeBlockTimeStamp(); TimeStamp nextCodeBlockTimeStamp();
bool isFastParseBlock(int p_blockNum) const; bool isFastParseBlock(int p_blockNum) const;
void clearFastParseResult();
static VTextBlockData *getBlockData(const QTextBlock &p_block); static VTextBlockData *getBlockData(const QTextBlock &p_block);
static bool isEmptyCodeBlockHighlights(const QVector<QVector<HLUnitStyle>> &p_highlights); static bool isEmptyCodeBlockHighlights(const QVector<QVector<HLUnitStyle>> &p_highlights);
@ -179,10 +191,14 @@ private:
QTimer *m_fastParseTimer; QTimer *m_fastParseTimer;
QTimer *m_scrollRehighlightTimer;
QTimer *m_rehighlightTimer; QTimer *m_rehighlightTimer;
// Blocks have only one format set which occupies the whole block. // Blocks have only one format set which occupies the whole block.
QSet<int> m_singleFormatBlocks; QSet<int> m_singleFormatBlocks;
bool m_notifyHighlightComplete;
}; };
inline const QVector<VElementRegion> &PegMarkdownHighlighter::getHeaderRegions() const inline const QVector<VElementRegion> &PegMarkdownHighlighter::getHeaderRegions() const

View File

@ -210,9 +210,10 @@ private:
QVector<HLUnit> m_blockHighlightCache; QVector<HLUnit> m_blockHighlightCache;
// Code block highlight cache. // Code block highlight cache.
// This cache is always valid.
QVector<HLUnitStyle> m_codeBlockHighlightCache; QVector<HLUnitStyle> m_codeBlockHighlightCache;
// Whether the above two cahces are valid. // Whether the highlight cache is valid.
bool m_cacheValid; bool m_cacheValid;
// Sorted by m_imageInfo.m_startPos, with no two element's position intersected. // Sorted by m_imageInfo.m_startPos, with no two element's position intersected.
@ -289,8 +290,7 @@ inline void VTextBlockData::setBlockHighlightCache(const QVector<HLUnit> &p_high
inline bool VTextBlockData::isCodeBlockHighlightCacheMatched(const QVector<HLUnitStyle> &p_highlight) const inline bool VTextBlockData::isCodeBlockHighlightCacheMatched(const QVector<HLUnitStyle> &p_highlight) const
{ {
if (!m_cacheValid if (p_highlight.size() != m_codeBlockHighlightCache.size()) {
|| p_highlight.size() != m_codeBlockHighlightCache.size()) {
return false; return false;
} }