PegMarkdownHighlighter: improve performance

This commit is contained in:
Le Tan 2018-07-27 21:05:32 +08:00
parent 9b49de3ab5
commit 8679ffa051
13 changed files with 403 additions and 131 deletions

View File

@ -22,6 +22,7 @@ PegHighlighterFastResult::PegHighlighterFastResult(const PegMarkdownHighlighter
PegHighlighterResult::PegHighlighterResult()
: m_timeStamp(0),
m_numOfBlocks(0),
m_codeBlockTimeStamp(0),
m_numOfCodeBlockHighlightsToRecv(0)
{
m_codeBlockStartExp = QRegExp(VUtils::c_fencedCodeBlockStartRegExp);
@ -32,6 +33,7 @@ PegHighlighterResult::PegHighlighterResult(const PegMarkdownHighlighter *p_peg,
const QSharedPointer<PegParseResult> &p_result)
: m_timeStamp(p_result->m_timeStamp),
m_numOfBlocks(p_result->m_numOfBlocks),
m_codeBlockTimeStamp(0),
m_numOfCodeBlockHighlightsToRecv(0)
{
m_codeBlockStartExp = QRegExp(VUtils::c_fencedCodeBlockStartRegExp);

View File

@ -54,6 +54,9 @@ public:
// Support fenced code block only.
QVector<QVector<HLUnitStyle> > m_codeBlocksHighlights;
// Timestamp for m_codeBlocksHighlights.
TimeStamp m_codeBlockTimeStamp;
// All image link regions.
QVector<VElementRegion> m_imageRegions;

View File

@ -2,17 +2,22 @@
#include <QTextDocument>
#include <QTimer>
#include <QScrollBar>
#include "pegparser.h"
#include "vconfigmanager.h"
#include "utils/vutils.h"
#include "utils/veditutils.h"
#include "vmdeditor.h"
extern VConfigManager *g_config;
PegMarkdownHighlighter::PegMarkdownHighlighter(QTextDocument *p_doc)
#define LARGE_BLOCK_NUMBER 2000
PegMarkdownHighlighter::PegMarkdownHighlighter(QTextDocument *p_doc, VMdEditor *p_editor)
: QSyntaxHighlighter(p_doc),
m_doc(p_doc),
m_editor(p_editor),
m_timeStamp(0),
m_parser(NULL),
m_parserExts(pmh_EXT_NOTES | pmh_EXT_STRIKE | pmh_EXT_FRONTMATTER | pmh_EXT_MARK)
@ -75,15 +80,33 @@ void PegMarkdownHighlighter::init(const QVector<HighlightingStyle> &p_styles,
const QVector<QVector<HLUnit>> &hls = result->m_blocksHighlights;
for (int i = 0; i < hls.size(); ++i) {
if (!hls[i].isEmpty()) {
rehighlightBlock(m_doc->findBlockByNumber(i));
QTextBlock block = m_doc->findBlockByNumber(i);
if (PegMarkdownHighlighter::blockTimeStamp(block) != m_timeStamp) {
rehighlightBlock(block);
}
}
}
});
m_rehighlightTimer = new QTimer(this);
m_rehighlightTimer->setSingleShot(true);
m_rehighlightTimer->setInterval(5);
connect(m_rehighlightTimer, &QTimer::timeout,
this, [this]() {
if (m_result->m_numOfBlocks > LARGE_BLOCK_NUMBER) {
rehighlightSensitiveBlocks();
}
});
connect(m_doc, &QTextDocument::contentsChange,
this, &PegMarkdownHighlighter::handleContentsChange);
connect(m_editor->verticalScrollBar(), &QScrollBar::valueChanged,
m_rehighlightTimer, static_cast<void(QTimer::*)()>(&QTimer::start));
}
// Just use parse results to highlight block.
// Do not maintain block data and state here.
void PegMarkdownHighlighter::highlightBlock(const QString &p_text)
{
QSharedPointer<PegHighlighterResult> result(m_result);
@ -93,27 +116,24 @@ void PegMarkdownHighlighter::highlightBlock(const QString &p_text)
if (result->matched(m_timeStamp)) {
preHighlightSingleFormatBlock(result->m_blocksHighlights, blockNum, p_text);
highlightBlockOne(result->m_blocksHighlights, blockNum);
} else {
preHighlightSingleFormatBlock(m_fastResult->m_blocksHighlights, blockNum, p_text);
// If fast result cover this block, we do not need to use the outdated one.
if (!highlightBlockOne(m_fastResult->m_blocksHighlights, blockNum)) {
highlightBlockOne(result->m_blocksHighlights, blockNum);
}
}
highlightBlockOne(result->m_blocksHighlights, blockNum);
// The complete result is not ready yet. We use fast result for compensation.
if (!result->matched(m_timeStamp)) {
highlightBlockOne(m_fastResult->m_blocksHighlights, blockNum);
}
// Set current block's user data.
updateBlockUserData(blockNum, p_text);
updateBlockUserState(result, blockNum, p_text);
if (currentBlockState() == HighlightBlockState::CodeBlock) {
highlightCodeBlock(result, blockNum, p_text);
highlightCodeBlockColorColumn(p_text);
PegMarkdownHighlighter::updateBlockCodeBlockTimeStamp(block, result->m_codeBlockTimeStamp);
}
PegMarkdownHighlighter::updateBlockTimeStamp(block, result->m_timeStamp);
}
void PegMarkdownHighlighter::preHighlightSingleFormatBlock(const QVector<QVector<HLUnit>> &p_highlights,
@ -140,13 +160,15 @@ void PegMarkdownHighlighter::preHighlightSingleFormatBlock(const QVector<QVector
}
}
void PegMarkdownHighlighter::highlightBlockOne(const QVector<QVector<HLUnit>> &p_highlights,
bool PegMarkdownHighlighter::highlightBlockOne(const QVector<QVector<HLUnit>> &p_highlights,
int p_blockNum)
{
bool highlighted = false;
if (p_highlights.size() > p_blockNum) {
// units are sorted by start position and length.
const QVector<HLUnit> &units = p_highlights[p_blockNum];
if (!units.isEmpty()) {
highlighted = true;
for (int i = 0; i < units.size(); ++i) {
const HLUnit &unit = units[i];
if (i == 0) {
@ -174,6 +196,8 @@ void PegMarkdownHighlighter::highlightBlockOne(const QVector<QVector<HLUnit>> &p
}
}
}
return highlighted;
}
// highlightBlock() will be called before this function.
@ -187,7 +211,9 @@ void PegMarkdownHighlighter::handleContentsChange(int p_position, int p_charsRem
++m_timeStamp;
startFastParse(p_position, p_charsRemoved, p_charsAdded);
if (m_timeStamp > 2) {
startFastParse(p_position, p_charsRemoved, p_charsAdded);
}
// We still need a timer to start a complete parse.
m_timer->start();
@ -266,7 +292,8 @@ void PegMarkdownHighlighter::setCodeBlockHighlights(TimeStamp p_timeStamp,
const QVector<HLUnitPos> &p_units)
{
QSharedPointer<PegHighlighterResult> result(m_result);
if (!result->matched(p_timeStamp)) {
if (!result->matched(p_timeStamp)
|| result->m_numOfCodeBlockHighlightsToRecv <= 0) {
return;
}
@ -330,25 +357,22 @@ void PegMarkdownHighlighter::setCodeBlockHighlights(TimeStamp p_timeStamp,
exit:
if (--result->m_numOfCodeBlockHighlightsToRecv <= 0) {
// Rehighlight specific blocks.
const QVector<QVector<HLUnitStyle>> &hls = result->m_codeBlocksHighlights;
for (int i = 0; i < hls.size(); ++i) {
if (!hls[i].isEmpty()) {
rehighlightBlock(m_doc->findBlockByNumber(i));
}
}
++result->m_codeBlockTimeStamp;
rehighlightBlocks();
}
}
void PegMarkdownHighlighter::updateHighlightFast()
{
updateHighlight();
}
void PegMarkdownHighlighter::updateHighlight()
{
m_timer->stop();
startParse();
if (m_result->matched(m_timeStamp)) {
// No need to parse again. Already the latest.
updateCodeBlocks(m_result);
rehighlightBlocks();
completeHighlight(m_result);
} else {
startParse();
}
}
void PegMarkdownHighlighter::handleParseResult(const QSharedPointer<PegParseResult> &p_result)
@ -360,14 +384,22 @@ void PegMarkdownHighlighter::handleParseResult(const QSharedPointer<PegParseResu
m_result.reset(new PegHighlighterResult(this, p_result));
m_singleFormatBlocks.clear();
updateSingleFormatBlocks(m_result->m_blocksHighlights);
updateCodeBlocks(m_result);
bool matched = m_result->matched(m_timeStamp);
if (matched) {
clearAllBlocksUserDataAndState(m_result);
rehighlight();
updateAllBlocksUserState(m_result);
completeHighlight(m_result);
updateCodeBlocks(m_result);
}
rehighlightBlocks();
if (matched) {
completeHighlight(m_result);
}
}
void PegMarkdownHighlighter::updateSingleFormatBlocks(const QVector<QVector<HLUnit>> &p_highlights)
@ -386,13 +418,11 @@ void PegMarkdownHighlighter::updateSingleFormatBlocks(const QVector<QVector<HLUn
}
}
void PegMarkdownHighlighter::updateCodeBlocks(QSharedPointer<PegHighlighterResult> p_result)
void PegMarkdownHighlighter::updateCodeBlocks(const QSharedPointer<PegHighlighterResult> &p_result)
{
if (!p_result->matched(m_timeStamp)) {
return;
}
if (g_config->getEnableCodeBlockHighlight()) {
// Only need to receive code block highlights when it is empty.
if (g_config->getEnableCodeBlockHighlight()
&& PegMarkdownHighlighter::isEmptyCodeBlockHighlights(p_result->m_codeBlocksHighlights)) {
p_result->m_codeBlocksHighlights.resize(p_result->m_numOfBlocks);
p_result->m_numOfCodeBlockHighlightsToRecv = p_result->m_codeBlocks.size();
}
@ -400,84 +430,102 @@ void PegMarkdownHighlighter::updateCodeBlocks(QSharedPointer<PegHighlighterResul
emit codeBlocksUpdated(p_result->m_timeStamp, p_result->m_codeBlocks);
}
void PegMarkdownHighlighter::updateBlockUserData(int p_blockNum, const QString &p_text)
void PegMarkdownHighlighter::clearAllBlocksUserDataAndState(const QSharedPointer<PegHighlighterResult> &p_result)
{
Q_UNUSED(p_text);
VTextBlockData *blockData = currentBlockData();
if (!blockData) {
blockData = new VTextBlockData();
setCurrentBlockUserData(blockData);
} else {
blockData->setCodeBlockIndentation(-1);
}
QTextBlock block = m_doc->firstBlock();
while (block.isValid()) {
clearBlockUserData(p_result, block);
if (blockData->getPreviews().isEmpty()) {
m_possiblePreviewBlocks.remove(p_blockNum);
} else {
m_possiblePreviewBlocks.insert(p_blockNum);
block.setUserState(HighlightBlockState::Normal);
block = block.next();
}
}
void PegMarkdownHighlighter::updateBlockUserState(const QSharedPointer<PegHighlighterResult> &p_result,
int p_blockNum,
const QString &p_text)
void PegMarkdownHighlighter::clearBlockUserData(const QSharedPointer<PegHighlighterResult> &p_result,
QTextBlock &p_block)
{
// Newly-added block.
if (currentBlockState() == -1) {
setCurrentBlockState(HighlightBlockState::Normal);
Q_UNUSED(p_result);
int blockNum = p_block.blockNumber();
VTextBlockData *blockData = static_cast<VTextBlockData *>(p_block.userData());
if (!blockData) {
blockData = new VTextBlockData();
p_block.setUserData(blockData);
m_possiblePreviewBlocks.remove(blockNum);
} else {
blockData->setCodeBlockIndentation(-1);
if (blockData->getPreviews().isEmpty()) {
m_possiblePreviewBlocks.remove(blockNum);
} else {
m_possiblePreviewBlocks.insert(blockNum);
}
}
}
if (!p_result->matched(m_timeStamp)) {
return;
}
void PegMarkdownHighlighter::updateAllBlocksUserState(const QSharedPointer<PegHighlighterResult> &p_result)
{
// Code blocks.
bool hlColumn = g_config->getColorColumn() > 0;
const QHash<int, HighlightBlockState> &cbStates = p_result->m_codeBlocksState;
for (auto it = cbStates.begin(); it != cbStates.end(); ++it) {
QTextBlock block = m_doc->findBlockByNumber(it.key());
if (!block.isValid()) {
continue;
}
HighlightBlockState state = HighlightBlockState::Normal;
auto it = p_result->m_codeBlocksState.find(p_blockNum);
if (it != p_result->m_codeBlocksState.end()) {
VTextBlockData *blockData = currentBlockData();
Q_ASSERT(blockData);
state = it.value();
// Set code block indentation.
switch (state) {
case HighlightBlockState::CodeBlockStart:
{
int startLeadingSpaces = 0;
QRegExp reg(VUtils::c_fencedCodeBlockStartRegExp);
int idx = reg.indexIn(p_text);
if (idx >= 0) {
startLeadingSpaces = reg.capturedTexts()[1].size();
if (hlColumn) {
VTextBlockData *blockData = static_cast<VTextBlockData *>(block.userData());
Q_ASSERT(blockData);
switch (it.value()) {
case HighlightBlockState::CodeBlockStart:
{
int startLeadingSpaces = 0;
QRegExp reg(VUtils::c_fencedCodeBlockStartRegExp);
int idx = reg.indexIn(block.text());
if (idx >= 0) {
startLeadingSpaces = reg.capturedTexts()[1].size();
}
blockData->setCodeBlockIndentation(startLeadingSpaces);
break;
}
blockData->setCodeBlockIndentation(startLeadingSpaces);
break;
}
case HighlightBlockState::CodeBlock:
V_FALLTHROUGH;
case HighlightBlockState::CodeBlockEnd:
{
int startLeadingSpaces = 0;
VTextBlockData *preBlockData = previousBlockData(block);
if (preBlockData) {
startLeadingSpaces = preBlockData->getCodeBlockIndentation();
}
case HighlightBlockState::CodeBlock:
V_FALLTHROUGH;
case HighlightBlockState::CodeBlockEnd:
{
int startLeadingSpaces = 0;
VTextBlockData *preBlockData = previousBlockData();
if (preBlockData) {
startLeadingSpaces = preBlockData->getCodeBlockIndentation();
blockData->setCodeBlockIndentation(startLeadingSpaces);
break;
}
blockData->setCodeBlockIndentation(startLeadingSpaces);
break;
default:
Q_ASSERT(false);
break;
}
}
default:
Q_ASSERT(false);
break;
}
} else if (p_result->m_hruleBlocks.contains(p_blockNum)) {
state = HighlightBlockState::HRule;
block.setUserState(it.value());
}
// Set code block state.
setCurrentBlockState(state);
// HRule blocks.
foreach (int blk, p_result->m_hruleBlocks) {
QTextBlock block = m_doc->findBlockByNumber(blk);
if (block.isValid()) {
block.setUserState(HighlightBlockState::HRule);
}
}
}
void PegMarkdownHighlighter::highlightCodeBlock(const QSharedPointer<PegHighlighterResult> &p_result,
@ -552,10 +600,6 @@ void PegMarkdownHighlighter::highlightCodeBlockColorColumn(const QString &p_text
void PegMarkdownHighlighter::completeHighlight(QSharedPointer<PegHighlighterResult> p_result)
{
if (!p_result->matched(m_timeStamp)) {
return;
}
if (isMathJaxEnabled()) {
emit mathjaxBlocksUpdated(p_result->m_mathjaxBlocks);
}
@ -572,7 +616,7 @@ void PegMarkdownHighlighter::getFastParseBlockRange(int p_position,
int &p_firstBlock,
int &p_lastBlock) const
{
const int maxNumOfBlocks = 500;
const int maxNumOfBlocks = 100;
int charsChanged = p_charsRemoved + p_charsAdded;
QTextBlock firstBlock = m_doc->findBlock(p_position);
@ -673,3 +717,67 @@ void PegMarkdownHighlighter::getFastParseBlockRange(int p_position,
p_firstBlock = p_lastBlock = -1;
}
}
void PegMarkdownHighlighter::rehighlightSensitiveBlocks()
{
QTextBlock cb = m_editor->textCursorW().block();
int first, last;
m_editor->visibleBlockRange(first, last);
bool cursorVisible = cb.blockNumber() >= first && cb.blockNumber() <= last;
// Include extra blocks.
const int nrUpExtra = 5;
const int nrDownExtra = 20;
first = qMax(0, first - nrUpExtra);
last = qMin(m_doc->blockCount() - 1, last + nrDownExtra);
if (rehighlightBlockRange(first, last)) {
if (cursorVisible) {
m_editor->ensureCursorVisibleW();
}
}
}
void PegMarkdownHighlighter::rehighlightBlocks()
{
if (m_result->m_numOfBlocks <= LARGE_BLOCK_NUMBER) {
rehighlightBlockRange(0, m_result->m_numOfBlocks - 1);
} else {
rehighlightSensitiveBlocks();
}
}
bool PegMarkdownHighlighter::rehighlightBlockRange(int p_first, int p_last)
{
bool highlighted = false;
const QHash<int, HighlightBlockState> &cbStates = m_result->m_codeBlocksState;
QTextBlock block = m_doc->findBlockByNumber(p_first);
while (block.isValid()) {
int blockNum = block.blockNumber();
if (blockNum > p_last) {
break;
}
bool needHL = PegMarkdownHighlighter::blockTimeStamp(block) != m_result->m_timeStamp;
if (!needHL) {
auto it = cbStates.find(blockNum);
if (it != cbStates.end()
&& it.value() == HighlightBlockState::CodeBlock
&& PegMarkdownHighlighter::blockCodeBlockTimeStamp(block) != m_result->m_codeBlockTimeStamp) {
needHL = true;
}
}
if (needHL) {
highlighted = true;
rehighlightBlock(block);
}
block = block.next();
}
return highlighted;
}

View File

@ -10,12 +10,13 @@
class PegParser;
class QTimer;
class VMdEditor;
class PegMarkdownHighlighter : public QSyntaxHighlighter
{
Q_OBJECT
public:
explicit PegMarkdownHighlighter(QTextDocument *p_doc = nullptr);
PegMarkdownHighlighter(QTextDocument *p_doc, VMdEditor *p_editor);
void init(const QVector<HighlightingStyle> &p_styles,
const QHash<QString, QTextCharFormat> &p_codeBlockStyles,
@ -33,9 +34,6 @@ public:
void addPossiblePreviewBlock(int p_blockNumber);
// Parse and only update the highlight results for rehighlight().
void updateHighlightFast();
QHash<QString, QTextCharFormat> &getCodeBlockStyles();
QVector<HighlightingStyle> &getStyles();
@ -48,6 +46,10 @@ public slots:
// Parse and rehighlight immediately.
void updateHighlight();
// Rehighlight sensitive blocks using current parse result, mainly
// visible blocks.
void rehighlightSensitiveBlocks();
signals:
void highlightCompleted();
@ -76,14 +78,14 @@ private:
void startFastParse(int p_position, int p_charsRemoved, int p_charsAdded);
void updateCodeBlocks(QSharedPointer<PegHighlighterResult> p_result);
void clearAllBlocksUserDataAndState(const QSharedPointer<PegHighlighterResult> &p_result);
// Set the user data of currentBlock().
void updateBlockUserData(int p_blockNum, const QString &p_text);
void updateAllBlocksUserState(const QSharedPointer<PegHighlighterResult> &p_result);
void updateBlockUserState(const QSharedPointer<PegHighlighterResult> &p_result,
int p_blockNum,
const QString &p_text);
void updateCodeBlocks(const QSharedPointer<PegHighlighterResult> &p_result);
void clearBlockUserData(const QSharedPointer<PegHighlighterResult> &p_result,
QTextBlock &p_block);
// Highlight fenced code block according to VCodeBlockHighlightHelper result.
void highlightCodeBlock(const QSharedPointer<PegHighlighterResult> &p_result,
@ -97,6 +99,8 @@ private:
VTextBlockData *previousBlockData() const;
VTextBlockData *previousBlockData(const QTextBlock &p_block) const;
void completeHighlight(QSharedPointer<PegHighlighterResult> p_result);
bool isMathJaxEnabled() const;
@ -109,7 +113,7 @@ private:
void processFastParseResult(const QSharedPointer<PegParseResult> &p_result);
void highlightBlockOne(const QVector<QVector<HLUnit>> &p_highlights,
bool highlightBlockOne(const QVector<QVector<HLUnit>> &p_highlights,
int p_blockNum);
// To avoid line height jitter.
@ -119,8 +123,24 @@ private:
void updateSingleFormatBlocks(const QVector<QVector<HLUnit>> &p_highlights);
void rehighlightBlocks();
bool rehighlightBlockRange(int p_first, int p_last);
static bool isEmptyCodeBlockHighlights(const QVector<QVector<HLUnitStyle>> &p_highlights);
static TimeStamp blockTimeStamp(const QTextBlock &p_block);
static void updateBlockTimeStamp(const QTextBlock &p_block, TimeStamp p_ts);
static TimeStamp blockCodeBlockTimeStamp(const QTextBlock &p_block);
static void updateBlockCodeBlockTimeStamp(const QTextBlock &p_block, TimeStamp p_ts);
QTextDocument *m_doc;
VMdEditor *m_editor;
TimeStamp m_timeStamp;
QVector<HighlightingStyle> m_styles;
@ -146,6 +166,8 @@ private:
QTimer *m_fastParseTimer;
QTimer *m_rehighlightTimer;
// Blocks have only one format set which occupies the whole block.
QSet<int> m_singleFormatBlocks;
};
@ -207,8 +229,75 @@ inline VTextBlockData *PegMarkdownHighlighter::previousBlockData() const
return static_cast<VTextBlockData *>(block.userData());
}
inline VTextBlockData *PegMarkdownHighlighter::previousBlockData(const QTextBlock &p_block) const
{
if (!p_block.isValid()) {
return NULL;
}
QTextBlock block = p_block.previous();
if (!block.isValid()) {
return NULL;
}
return static_cast<VTextBlockData *>(block.userData());
}
inline bool PegMarkdownHighlighter::isMathJaxEnabled() const
{
return m_parserExts & pmh_EXT_MATH;
}
inline TimeStamp PegMarkdownHighlighter::blockTimeStamp(const QTextBlock &p_block)
{
VTextBlockData *data = static_cast<VTextBlockData *>(p_block.userData());
if (data) {
return data->getTimeStamp();
} else {
return 0;
}
}
inline void PegMarkdownHighlighter::updateBlockTimeStamp(const QTextBlock &p_block, TimeStamp p_ts)
{
VTextBlockData *data = static_cast<VTextBlockData *>(p_block.userData());
if (data) {
data->setTimeStamp(p_ts);
}
}
inline TimeStamp PegMarkdownHighlighter::blockCodeBlockTimeStamp(const QTextBlock &p_block)
{
VTextBlockData *data = static_cast<VTextBlockData *>(p_block.userData());
if (data) {
return data->getCodeBlockTimeStamp();
} else {
return 0;
}
}
inline void PegMarkdownHighlighter::updateBlockCodeBlockTimeStamp(const QTextBlock &p_block, TimeStamp p_ts)
{
VTextBlockData *data = static_cast<VTextBlockData *>(p_block.userData());
if (data) {
data->setCodeBlockTimeStamp(p_ts);
}
}
inline bool PegMarkdownHighlighter::isEmptyCodeBlockHighlights(const QVector<QVector<HLUnitStyle>> &p_highlights)
{
if (p_highlights.isEmpty()) {
return true;
}
bool empty = true;
for (int i = 0; i < p_highlights.size(); ++i) {
if (!p_highlights[i].isEmpty()) {
empty = false;
break;
}
}
return empty;
}
#endif // PEGMARKDOWNHIGHLIGHTER_H

View File

@ -1620,3 +1620,20 @@ bool VUtils::onlyHasImgInHtml(const QString &p_html)
QRegExp reg("<(?:p|span|div) ");
return !p_html.contains(reg);
}
int VUtils::elapsedTime(bool p_reset)
{
static QTime tm;
if (p_reset) {
tm = QTime();
return 0;
}
if (tm.isNull()) {
tm.start();
return 0;
}
return tm.restart();
}

View File

@ -54,6 +54,10 @@ class QFormLayout;
# define V_FALLTHROUGH while(false){}
#endif
#define DETIME() qDebug() << "ELAPSED_TIME" << __func__ << __LINE__ << VUtils::elapsedTime()
#define RESET_TIME() VUtils::elapsedTime(true)
enum class MessageBoxType
{
Normal = 0,
@ -350,6 +354,8 @@ public:
// Whether @p_html has only <img> content.
static bool onlyHasImgInHtml(const QString &p_html);
static int elapsedTime(bool p_reset = false);
// Regular expression for image link.
// ![image title]( http://github.com/tamlok/vnote.jpg "alt text" =200x100)
// Captured texts (need to be trimmed):

View File

@ -114,7 +114,7 @@ enum class PreviewImageSource { Image, CodeBlock, Invalid };
enum HighlightBlockState
{
Normal = 0,
Normal = -1,
// A fenced code block.
CodeBlockStart,

View File

@ -199,6 +199,8 @@ public:
virtual void zoomOutW(int p_range = 1) = 0;
virtual void ensureCursorVisibleW() = 0;
protected:
void init();

View File

@ -60,7 +60,7 @@ VMdEditor::VMdEditor(VFile *p_file,
setReadOnly(true);
m_pegHighlighter = new PegMarkdownHighlighter(document());
m_pegHighlighter = new PegMarkdownHighlighter(document(), this);
m_pegHighlighter->init(g_config->getMdHighlightingStyles(),
g_config->getCodeBlockStyles(),
g_config->getEnableMathjax(),
@ -182,13 +182,13 @@ void VMdEditor::reloadFile()
const QString &content = m_file->getContent();
setPlainText(content);
setModified(false);
m_pegHighlighter->updateHighlightFast();
m_freshEdit = true;
setReadOnly(readonly);
refreshPreview();
if (!m_freshEdit) {
m_freshEdit = true;
refreshPreview();
}
}
bool VMdEditor::scrollToBlock(int p_blockNumber)

View File

@ -184,6 +184,11 @@ public:
zoomPage(false, p_range);
}
void ensureCursorVisibleW() Q_DECL_OVERRIDE
{
ensureCursorVisible();
}
signals:
// Signal when headers change.
void headersChanged(const QVector<VTableOfContentItem> &p_headers);

View File

@ -381,8 +381,10 @@ void VPreviewManager::updateBlockPreviewInfo(TS p_timeStamp,
continue;
}
VTextBlockData *blockData = dynamic_cast<VTextBlockData *>(block.userData());
Q_ASSERT(blockData);
VTextBlockData *blockData = static_cast<VTextBlockData *>(block.userData());
if (!blockData) {
continue;
}
VPreviewInfo *info = new VPreviewInfo(PreviewSource::ImageLink,
p_timeStamp,
@ -425,8 +427,11 @@ void VPreviewManager::updateBlockPreviewInfo(TS p_timeStamp,
continue;
}
VTextBlockData *blockData = dynamic_cast<VTextBlockData *>(block.userData());
Q_ASSERT(blockData);
VTextBlockData *blockData = static_cast<VTextBlockData *>(block.userData());
if (!blockData) {
continue;
}
VPreviewInfo *info = new VPreviewInfo(p_source,
p_timeStamp,
img->m_startPos - img->m_blockPos,
@ -476,7 +481,7 @@ void VPreviewManager::clearBlockObsoletePreviewInfo(long long p_timeStamp,
continue;
}
VTextBlockData *blockData = dynamic_cast<VTextBlockData *>(block.userData());
VTextBlockData *blockData = static_cast<VTextBlockData *>(block.userData());
if (!blockData) {
continue;
}
@ -550,7 +555,7 @@ void VPreviewManager::checkBlocksForObsoletePreview(const QList<int> &p_blocks)
continue;
}
VTextBlockData *blockData = dynamic_cast<VTextBlockData *>(block.userData());
VTextBlockData *blockData = static_cast<VTextBlockData *>(block.userData());
if (!blockData) {
continue;
}

View File

@ -1,9 +1,9 @@
#include "vtextblockdata.h"
#include <QDebug>
VTextBlockData::VTextBlockData()
: QTextBlockUserData(),
m_timeStamp(0),
m_codeBlockTimeStamp(0),
m_codeBlockIndentation(-1)
{
}
@ -90,7 +90,6 @@ bool VTextBlockData::clearObsoletePreview(long long p_timeStamp, PreviewSource p
if (ele->m_source == p_source
&& ele->m_timeStamp != p_timeStamp) {
// Remove it.
qDebug() << "clear obsolete preview" << ele->m_imageInfo.toString();
delete ele;
it = m_previews.erase(it);
deleted = true;

View File

@ -4,6 +4,8 @@
#include <QTextBlockUserData>
#include <QVector>
#include "vconstants.h"
// Sources of the preview.
enum class PreviewSource
{
@ -163,10 +165,24 @@ public:
void setCodeBlockIndentation(int p_indent);
TimeStamp getTimeStamp() const;
void setTimeStamp(TimeStamp p_ts);
TimeStamp getCodeBlockTimeStamp() const;
void setCodeBlockTimeStamp(TimeStamp p_ts);
private:
// Check the order of elements.
bool checkOrder() const;
// TimeStamp of the highlight result which has been applied to this block.
TimeStamp m_timeStamp;
// TimeStamp of the code block highlight result which has been applied to this block.
TimeStamp m_codeBlockTimeStamp;
// Sorted by m_imageInfo.m_startPos, with no two element's position intersected.
QVector<VPreviewInfo *> m_previews;
@ -188,4 +204,24 @@ inline void VTextBlockData::setCodeBlockIndentation(int p_indent)
{
m_codeBlockIndentation = p_indent;
}
inline TimeStamp VTextBlockData::getTimeStamp() const
{
return m_timeStamp;
}
inline void VTextBlockData::setTimeStamp(TimeStamp p_ts)
{
m_timeStamp = p_ts;
}
inline TimeStamp VTextBlockData::getCodeBlockTimeStamp() const
{
return m_codeBlockTimeStamp;
}
inline void VTextBlockData::setCodeBlockTimeStamp(TimeStamp p_ts)
{
m_codeBlockTimeStamp = p_ts;
}
#endif // VTEXTBLOCKDATA_H