optimize VImagePreviewer by flags

This commit is contained in:
Le Tan 2017-09-08 21:41:46 +08:00
parent e55b0af00e
commit 07e8f27776
5 changed files with 77 additions and 27 deletions

View File

@ -80,7 +80,7 @@ HGMarkdownHighlighter::~HGMarkdownHighlighter()
} }
} }
void HGMarkdownHighlighter::updateBlockUserData(const QString &p_text) void HGMarkdownHighlighter::updateBlockUserData(int p_blockNum, const QString &p_text)
{ {
VTextBlockData *blockData = dynamic_cast<VTextBlockData *>(currentBlockUserData()); VTextBlockData *blockData = dynamic_cast<VTextBlockData *>(currentBlockUserData());
if (!blockData) { if (!blockData) {
@ -88,7 +88,33 @@ void HGMarkdownHighlighter::updateBlockUserData(const QString &p_text)
setCurrentBlockUserData(blockData); setCurrentBlockUserData(blockData);
} }
blockData->setContainsPreviewImage(p_text.contains(QChar::ObjectReplacementCharacter)); bool contains = p_text.contains(QChar::ObjectReplacementCharacter);
blockData->setContainsPreviewImage(contains);
auto curIt = m_potentialPreviewBlocks.find(p_blockNum);
if (curIt == m_potentialPreviewBlocks.end()) {
if (contains) {
m_potentialPreviewBlocks.insert(p_blockNum, true);
}
} else {
if (!contains) {
m_potentialPreviewBlocks.erase(curIt);
}
}
// Delete elements beyond current block count.
int blocks = document->blockCount();
if (!m_potentialPreviewBlocks.isEmpty()) {
auto it = m_potentialPreviewBlocks.end();
do {
--it;
if (it.key() >= blocks) {
it = m_potentialPreviewBlocks.erase(it);
} else {
break;
}
} while (it != m_potentialPreviewBlocks.begin());
}
} }
void HGMarkdownHighlighter::highlightBlock(const QString &text) void HGMarkdownHighlighter::highlightBlock(const QString &text)
@ -107,7 +133,7 @@ void HGMarkdownHighlighter::highlightBlock(const QString &text)
// We can use other highlighting methods to complement it. // We can use other highlighting methods to complement it.
// Set current block's user data. // Set current block's user data.
updateBlockUserData(text); updateBlockUserData(blockNum, text);
// If it is a block inside HTML comment, just skip it. // If it is a block inside HTML comment, just skip it.
if (isBlockInsideCommentRegion(currentBlock())) { if (isBlockInsideCommentRegion(currentBlock())) {

View File

@ -4,7 +4,7 @@
#include <QTextCharFormat> #include <QTextCharFormat>
#include <QSyntaxHighlighter> #include <QSyntaxHighlighter>
#include <QAtomicInt> #include <QAtomicInt>
#include <QSet> #include <QMap>
#include <QString> #include <QString>
#include <QHash> #include <QHash>
@ -118,9 +118,12 @@ public:
int waitInterval, int waitInterval,
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 QVector<HLUnitPos> &p_units); void setCodeBlockHighlights(const QVector<HLUnitPos> &p_units);
const QMap<int, bool> &getPotentialPreviewBlocks() const;
signals: signals:
void highlightCompleted(); void highlightCompleted();
@ -160,6 +163,11 @@ private:
int m_numOfCodeBlockHighlightsToRecv; int m_numOfCodeBlockHighlightsToRecv;
// If the ith block contains preview, then the set contains i.
// If the set contains i, the ith block may contain preview.
// We just use the key.
QMap<int, bool> m_potentialPreviewBlocks;
// All HTML comment regions. // All HTML comment regions.
QVector<VElementRegion> m_commentRegions; QVector<VElementRegion> m_commentRegions;
@ -210,7 +218,12 @@ private:
void highlightChanged(); void highlightChanged();
// Set the user data of currentBlock(). // Set the user data of currentBlock().
void updateBlockUserData(const QString &p_text); void updateBlockUserData(int p_blockNum, const QString &p_text);
}; };
inline const QMap<int, bool> &HGMarkdownHighlighter::getPotentialPreviewBlocks() const
{
return m_potentialPreviewBlocks;
}
#endif #endif

View File

@ -20,10 +20,10 @@ extern VConfigManager *g_config;
const int VImagePreviewer::c_minImageWidth = 100; const int VImagePreviewer::c_minImageWidth = 100;
VImagePreviewer::VImagePreviewer(VMdEdit *p_edit) VImagePreviewer::VImagePreviewer(VMdEdit *p_edit, const HGMarkdownHighlighter *p_highlighter)
: QObject(p_edit), m_edit(p_edit), m_document(p_edit->document()), : QObject(p_edit), m_edit(p_edit), m_document(p_edit->document()),
m_file(p_edit->getFile()), m_imageWidth(c_minImageWidth), m_file(p_edit->getFile()), m_highlighter(p_highlighter),
m_timeStamp(0), m_previewIndex(0), m_imageWidth(c_minImageWidth), m_timeStamp(0), m_previewIndex(0),
m_previewEnabled(g_config->getEnablePreviewImages()), m_isPreviewing(false) m_previewEnabled(g_config->getEnablePreviewImages()), m_isPreviewing(false)
{ {
m_updateTimer = new QTimer(this); m_updateTimer = new QTimer(this);
@ -152,19 +152,28 @@ void VImagePreviewer::clearObsoletePreviewImages(QTextCursor &p_cursor)
} }
} }
bool hasObsolete = false;
QTextBlock block = m_document->begin();
// Clean block data and delete obsolete preview. // Clean block data and delete obsolete preview.
while (block.isValid()) { bool hasObsolete = false;
if (!VTextBlockData::containsPreviewImage(block)) { int blockCount = m_document->blockCount();
block = block.next(); // Must copy it.
QMap<int, bool> potentialBlocks = m_highlighter->getPotentialPreviewBlocks();
// From back to front.
if (!potentialBlocks.isEmpty()) {
auto it = potentialBlocks.end();
do {
--it;
int blockNum = it.key();
if (blockNum >= blockCount) {
continue; continue;
} else { }
QTextBlock nextBlock = block.next();
QTextBlock block = m_document->findBlockByNumber(blockNum);
if (block.isValid()
&& VTextBlockData::containsPreviewImage(block)) {
// Notice the short circuit. // Notice the short circuit.
hasObsolete = clearObsoletePreviewImagesOfBlock(block, p_cursor) || hasObsolete; hasObsolete = clearObsoletePreviewImagesOfBlock(block, p_cursor) || hasObsolete;
block = nextBlock;
} }
} while (it != potentialBlocks.begin());
} }
if (hasObsolete) { if (hasObsolete) {
@ -246,11 +255,11 @@ bool VImagePreviewer::clearObsoletePreviewImagesOfBlock(QTextBlock &p_block,
return hasObsolete; return hasObsolete;
} }
// Returns true if p_text[p_start, p_end) is all spaces. // Returns true if p_text[p_start, p_end) is all spaces or QChar::ObjectReplacementCharacter.
static bool isAllSpaces(const QString &p_text, int p_start, int p_end) static bool isAllSpacesOrObject(const QString &p_text, int p_start, int p_end)
{ {
for (int i = p_start; i < p_end && i < p_text.size(); ++i) { for (int i = p_start; i < p_end && i < p_text.size(); ++i) {
if (!p_text[i].isSpace()) { if (!p_text[i].isSpace() && p_text[i] != QChar::ObjectReplacementCharacter) {
return false; return false;
} }
} }
@ -281,9 +290,9 @@ void VImagePreviewer::fetchImageLinksFromRegions(QVector<ImageLinkInfo> &p_image
Q_ASSERT(reg.m_endPos <= blockEnd); Q_ASSERT(reg.m_endPos <= blockEnd);
ImageLinkInfo info(reg.m_startPos, reg.m_endPos); ImageLinkInfo info(reg.m_startPos, reg.m_endPos);
if ((reg.m_startPos == blockStart if ((reg.m_startPos == blockStart
|| isAllSpaces(text, 0, reg.m_startPos - blockStart)) || isAllSpacesOrObject(text, 0, reg.m_startPos - blockStart))
&& (reg.m_endPos == blockEnd && (reg.m_endPos == blockEnd
|| isAllSpaces(text, reg.m_endPos - blockStart, blockEnd - blockStart))) { || isAllSpacesOrObject(text, reg.m_endPos - blockStart, blockEnd - blockStart))) {
// Image block. // Image block.
info.m_isBlock = true; info.m_isBlock = true;
info.m_linkUrl = fetchImagePathToPreview(text); info.m_linkUrl = fetchImagePathToPreview(text);

View File

@ -17,7 +17,7 @@ class VImagePreviewer : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit VImagePreviewer(VMdEdit *p_edit); explicit VImagePreviewer(VMdEdit *p_edit, const HGMarkdownHighlighter *p_highlighter);
// Whether @p_block is an image previewed block. // Whether @p_block is an image previewed block.
// The image previewed block is a block containing only the special character // The image previewed block is a block containing only the special character
@ -202,6 +202,8 @@ private:
QTextDocument *m_document; QTextDocument *m_document;
VFile *m_file; VFile *m_file;
const HGMarkdownHighlighter *m_highlighter;
// Map from image full path to QUrl identifier in the QTextDocument's cache. // Map from image full path to QUrl identifier in the QTextDocument's cache.
QHash<QString, ImageInfo> m_imageCache; QHash<QString, ImageInfo> m_imageCache;

View File

@ -44,7 +44,7 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
m_cbHighlighter = new VCodeBlockHighlightHelper(m_mdHighlighter, p_vdoc, m_cbHighlighter = new VCodeBlockHighlightHelper(m_mdHighlighter, p_vdoc,
p_type); p_type);
m_imagePreviewer = new VImagePreviewer(this); m_imagePreviewer = new VImagePreviewer(this, m_mdHighlighter);
connect(m_mdHighlighter, &HGMarkdownHighlighter::imageLinksUpdated, connect(m_mdHighlighter, &HGMarkdownHighlighter::imageLinksUpdated,
m_imagePreviewer, &VImagePreviewer::imageLinksChanged); m_imagePreviewer, &VImagePreviewer::imageLinksChanged);
connect(m_imagePreviewer, &VImagePreviewer::requestUpdateImageLinks, connect(m_imagePreviewer, &VImagePreviewer::requestUpdateImageLinks,