refactor VCodeBlockHighlightHelper

1. Use QVector instead of QList;
2. Use cache for highlight result;
This commit is contained in:
Le Tan 2017-09-02 12:09:49 +08:00
parent c3408769b0
commit 639d1cb9e9
4 changed files with 93 additions and 25 deletions

View File

@ -446,7 +446,7 @@ bool HGMarkdownHighlighter::updateCodeBlocks()
m_codeBlockHighlights[i].clear(); m_codeBlockHighlights[i].clear();
} }
QList<VCodeBlock> codeBlocks; QVector<VCodeBlock> codeBlocks;
VCodeBlock item; VCodeBlock item;
bool inBlock = false; bool inBlock = false;
@ -509,7 +509,7 @@ static bool HLUnitStyleComp(const HLUnitStyle &a, const HLUnitStyle &b)
} }
} }
void HGMarkdownHighlighter::setCodeBlockHighlights(const QList<HLUnitPos> &p_units) void HGMarkdownHighlighter::setCodeBlockHighlights(const QVector<HLUnitPos> &p_units)
{ {
if (p_units.isEmpty()) { if (p_units.isEmpty()) {
goto exit; goto exit;

View File

@ -5,7 +5,6 @@
#include <QSyntaxHighlighter> #include <QSyntaxHighlighter>
#include <QAtomicInt> #include <QAtomicInt>
#include <QSet> #include <QSet>
#include <QList>
#include <QString> #include <QString>
#include <QHash> #include <QHash>
@ -120,13 +119,13 @@ public:
QTextDocument *parent = 0); QTextDocument *parent = 0);
~HGMarkdownHighlighter(); ~HGMarkdownHighlighter();
// Request to update highlihgt (re-parse and re-highlight) // Request to update highlihgt (re-parse and re-highlight)
void setCodeBlockHighlights(const QList<HLUnitPos> &p_units); void setCodeBlockHighlights(const QVector<HLUnitPos> &p_units);
signals: signals:
void highlightCompleted(); void highlightCompleted();
// QList is implicitly shared. // QVector is implicitly shared.
void codeBlocksUpdated(const QList<VCodeBlock> &p_codeBlocks); void codeBlocksUpdated(const QVector<VCodeBlock> &p_codeBlocks);
// Emitted when image regions have been fetched from a new parsing result. // Emitted when image regions have been fetched from a new parsing result.
void imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions); void imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions);

View File

@ -15,6 +15,8 @@ VCodeBlockHighlightHelper::VCodeBlockHighlightHelper(HGMarkdownHighlighter *p_hi
this, &VCodeBlockHighlightHelper::handleCodeBlocksUpdated); this, &VCodeBlockHighlightHelper::handleCodeBlocksUpdated);
connect(m_vdocument, &VDocument::textHighlighted, connect(m_vdocument, &VDocument::textHighlighted,
this, &VCodeBlockHighlightHelper::handleTextHighlightResult); this, &VCodeBlockHighlightHelper::handleTextHighlightResult);
// Web side is ready for code block highlight.
connect(m_vdocument, &VDocument::readyToHighlightText, connect(m_vdocument, &VDocument::readyToHighlightText,
m_highlighter, &HGMarkdownHighlighter::updateHighlight); m_highlighter, &HGMarkdownHighlighter::updateHighlight);
} }
@ -52,14 +54,23 @@ QString VCodeBlockHighlightHelper::unindentCodeBlock(const QString &p_text)
return res; return res;
} }
void VCodeBlockHighlightHelper::handleCodeBlocksUpdated(const QList<VCodeBlock> &p_codeBlocks) void VCodeBlockHighlightHelper::handleCodeBlocksUpdated(const QVector<VCodeBlock> &p_codeBlocks)
{ {
int curStamp = m_timeStamp.fetchAndAddRelaxed(1) + 1; int curStamp = m_timeStamp.fetchAndAddRelaxed(1) + 1;
m_codeBlocks = p_codeBlocks; m_codeBlocks = p_codeBlocks;
for (int i = 0; i < m_codeBlocks.size(); ++i) { for (int i = 0; i < m_codeBlocks.size(); ++i) {
QString unindentedText = unindentCodeBlock(m_codeBlocks[i].m_text); const VCodeBlock &block = m_codeBlocks[i];
auto it = m_cache.find(block.m_text);
if (it != m_cache.end()) {
// Hit cache.
qDebug() << "code block highlight hit cache" << curStamp << i;
it.value().m_timeStamp = curStamp;
updateHighlightResults(block.m_startPos, it.value().m_units);
} else {
QString unindentedText = unindentCodeBlock(block.m_text);
m_vdocument->highlightTextAsync(unindentedText, i, curStamp); m_vdocument->highlightTextAsync(unindentedText, i, curStamp);
} }
}
} }
void VCodeBlockHighlightHelper::handleTextHighlightResult(const QString &p_html, void VCodeBlockHighlightHelper::handleTextHighlightResult(const QString &p_html,
@ -109,7 +120,7 @@ void VCodeBlockHighlightHelper::parseHighlightResult(int p_timeStamp,
int startPos = block.m_startPos; int startPos = block.m_startPos;
QString text = block.m_text; QString text = block.m_text;
QList<HLUnitPos> hlUnits; QVector<HLUnitPos> hlUnits;
bool failed = true; bool failed = true;
@ -153,7 +164,7 @@ void VCodeBlockHighlightHelper::parseHighlightResult(int p_timeStamp,
failed = true; failed = true;
goto exit; goto exit;
} }
if (!parseSpanElement(xml, startPos, text, textIndex, hlUnits)) { if (!parseSpanElement(xml, text, textIndex, hlUnits)) {
failed = true; failed = true;
goto exit; goto exit;
} }
@ -185,15 +196,27 @@ exit:
hlUnits.clear(); hlUnits.clear();
} }
// Add it to cache.
addToHighlightCache(text, p_timeStamp, hlUnits);
updateHighlightResults(startPos, hlUnits);
}
void VCodeBlockHighlightHelper::updateHighlightResults(int p_startPos,
QVector<HLUnitPos> p_units)
{
for (int i = 0; i < p_units.size(); ++i) {
p_units[i].m_position += p_startPos;
}
// We need to call this function anyway to trigger the rehighlight. // We need to call this function anyway to trigger the rehighlight.
m_highlighter->setCodeBlockHighlights(hlUnits); m_highlighter->setCodeBlockHighlights(p_units);
} }
bool VCodeBlockHighlightHelper::parseSpanElement(QXmlStreamReader &p_xml, bool VCodeBlockHighlightHelper::parseSpanElement(QXmlStreamReader &p_xml,
int p_startPos,
const QString &p_text, const QString &p_text,
int &p_index, int &p_index,
QList<HLUnitPos> &p_units) QVector<HLUnitPos> &p_units)
{ {
int unitStart = p_index; int unitStart = p_index;
QString style = p_xml.attributes().value("class").toString(); QString style = p_xml.attributes().value("class").toString();
@ -215,7 +238,7 @@ bool VCodeBlockHighlightHelper::parseSpanElement(QXmlStreamReader &p_xml,
} }
// Sub-span. // Sub-span.
if (!parseSpanElement(p_xml, p_startPos, p_text, p_index, p_units)) { if (!parseSpanElement(p_xml, p_text, p_index, p_units)) {
return false; return false;
} }
} else if (p_xml.isEndElement()) { } else if (p_xml.isEndElement()) {
@ -223,8 +246,8 @@ bool VCodeBlockHighlightHelper::parseSpanElement(QXmlStreamReader &p_xml,
return false; return false;
} }
// Got a complete span. // Got a complete span. Use relative position here.
HLUnitPos unit(unitStart + p_startPos, p_index - unitStart, style); HLUnitPos unit(unitStart, p_index - unitStart, style);
p_units.append(unit); p_units.append(unit);
return true; return true;
} else { } else {
@ -233,3 +256,24 @@ bool VCodeBlockHighlightHelper::parseSpanElement(QXmlStreamReader &p_xml,
} }
return false; return false;
} }
void VCodeBlockHighlightHelper::addToHighlightCache(const QString &p_text,
int p_timeStamp,
const QVector<HLUnitPos> &p_units)
{
const int c_maxEntries = 100;
const int c_maxTimeStampSpan = 3;
if (m_cache.size() >= c_maxEntries) {
// Remove the oldest one.
int ts = p_timeStamp - c_maxTimeStampSpan;
for (auto it = m_cache.begin(); it != m_cache.end();) {
if (it.value().m_timeStamp < ts) {
it = m_cache.erase(it);
} else {
++it;
}
}
}
m_cache.insert(p_text, HLResult(p_timeStamp, p_units));
}

View File

@ -2,9 +2,10 @@
#define VCODEBLOCKHIGHLIGHTHELPER_H #define VCODEBLOCKHIGHLIGHTHELPER_H
#include <QObject> #include <QObject>
#include <QList> #include <QVector>
#include <QAtomicInteger> #include <QAtomicInteger>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QHash>
#include "vconfigmanager.h" #include "vconfigmanager.h"
class VDocument; class VDocument;
@ -16,22 +17,36 @@ public:
VCodeBlockHighlightHelper(HGMarkdownHighlighter *p_highlighter, VCodeBlockHighlightHelper(HGMarkdownHighlighter *p_highlighter,
VDocument *p_vdoc, MarkdownConverterType p_type); VDocument *p_vdoc, MarkdownConverterType p_type);
signals:
private slots: private slots:
void handleCodeBlocksUpdated(const QList<VCodeBlock> &p_codeBlocks); void handleCodeBlocksUpdated(const QVector<VCodeBlock> &p_codeBlocks);
void handleTextHighlightResult(const QString &p_html, int p_id, int p_timeStamp); void handleTextHighlightResult(const QString &p_html, int p_id, int p_timeStamp);
private: private:
struct HLResult
{
HLResult() : m_timeStamp(-1)
{
}
HLResult(int p_timeStamp, const QVector<HLUnitPos> &p_units)
: m_timeStamp(p_timeStamp), m_units(p_units)
{
}
int m_timeStamp;
QVector<HLUnitPos> m_units;
};
void parseHighlightResult(int p_timeStamp, int p_idx, const QString &p_html); void parseHighlightResult(int p_timeStamp, int p_idx, const QString &p_html);
// @p_startPos: the global position of the start of the code block;
// @p_text: the raw text of the code block; // @p_text: the raw text of the code block;
// @p_index: the start index of the span element within @p_text; // @p_index: the start index of the span element within @p_text;
// @p_units: all the highlight units of this code block; // @p_units: all the highlight units of this code block;
bool parseSpanElement(QXmlStreamReader &p_xml, int p_startPos, bool parseSpanElement(QXmlStreamReader &p_xml,
const QString &p_text, int &p_index, const QString &p_text, int &p_index,
QList<HLUnitPos> &p_units); QVector<HLUnitPos> &p_units);
// @p_text: text of fenced code block. // @p_text: text of fenced code block.
// Get the indent level of the first line (fence) and unindent the whole block // Get the indent level of the first line (fence) and unindent the whole block
// to make the fence at the highest indent level. // to make the fence at the highest indent level.
@ -39,11 +54,21 @@ private:
// without any context. // without any context.
QString unindentCodeBlock(const QString &p_text); QString unindentCodeBlock(const QString &p_text);
void updateHighlightResults(int p_startPos, QVector<HLUnitPos> p_units);
void addToHighlightCache(const QString &p_text,
int p_timeStamp,
const QVector<HLUnitPos> &p_units);
HGMarkdownHighlighter *m_highlighter; HGMarkdownHighlighter *m_highlighter;
VDocument *m_vdocument; VDocument *m_vdocument;
MarkdownConverterType m_type; MarkdownConverterType m_type;
QAtomicInteger<int> m_timeStamp; QAtomicInteger<int> m_timeStamp;
QList<VCodeBlock> m_codeBlocks; QVector<VCodeBlock> m_codeBlocks;
// Cache for highlight result, using the code block text as key.
// The HLResult has relative position only.
QHash<QString, HLResult> m_cache;
}; };
#endif // VCODEBLOCKHIGHLIGHTHELPER_H #endif // VCODEBLOCKHIGHLIGHTHELPER_H