#ifndef PEGMARKDOWNHIGHLIGHTER_H #define PEGMARKDOWNHIGHLIGHTER_H #include #include #include #include "vtextblockdata.h" #include "markdownhighlighterdata.h" #include "peghighlighterresult.h" class PegParser; class QTimer; class VMdEditor; class PegMarkdownHighlighter : public QSyntaxHighlighter { Q_OBJECT public: PegMarkdownHighlighter(QTextDocument *p_doc, VMdEditor *p_editor); void init(const QVector &p_styles, const QHash &p_codeBlockStyles, bool p_mathjaxEnabled, int p_timerInterval); // Set code block highlight result by VCodeBlockHighlightHelper. void setCodeBlockHighlights(TimeStamp p_timeStamp, const QVector &p_units); const QVector &getHeaderRegions() const; const QSet &getPossiblePreviewBlocks() const; void clearPossiblePreviewBlocks(const QVector &p_blocksToClear); void addPossiblePreviewBlock(int p_blockNumber); QHash &getCodeBlockStyles(); QVector &getStyles(); const QVector &getStyles() const; const QTextDocument *getDocument() const; const QVector &getImageRegions() const; const QVector &getCodeBlocks() const; public slots: // Parse and rehighlight immediately. void updateHighlight(); // Rehighlight sensitive blocks using current parse result, mainly // visible blocks. void rehighlightSensitiveBlocks(); signals: void highlightCompleted(); // QVector is implicitly shared. void codeBlocksUpdated(TimeStamp p_timeStamp, const QVector &p_codeBlocks); // Emitted when image regions have been fetched from a new parsing result. void imageLinksUpdated(const QVector &p_imageRegions); // Emitted when header regions have been fetched from a new parsing result. void headersUpdated(const QVector &p_headerRegions); // Emitted when Mathjax blocks updated. void mathjaxBlocksUpdated(const QVector &p_mathjaxBlocks); // Emitted when table blocks updated. void tableBlocksUpdated(const QVector &p_tableBlocks); protected: void highlightBlock(const QString &p_text) Q_DECL_OVERRIDE; private slots: void handleContentsChange(int p_position, int p_charsRemoved, int p_charsAdded); void handleParseResult(const QSharedPointer &p_result); private: struct FastParseInfo { int m_position; int m_charsRemoved; int m_charsAdded; } m_fastParseInfo; void startParse(); void startFastParse(int p_position, int p_charsRemoved, int p_charsAdded); void clearAllBlocksUserDataAndState(const QSharedPointer &p_result); void updateAllBlocksUserState(const QSharedPointer &p_result); void updateCodeBlocks(const QSharedPointer &p_result); void clearBlockUserData(const QSharedPointer &p_result, QTextBlock &p_block); // Highlight fenced code block according to VCodeBlockHighlightHelper result. void highlightCodeBlock(const QSharedPointer &p_result, int p_blockNum, const QString &p_text, QVector *p_cache); void highlightCodeBlock(const QVector &p_units, const QString &p_text); void highlightCodeBlockOne(const QVector &p_units); // Highlight color column in code block. void highlightCodeBlockColorColumn(const QString &p_text); VTextBlockData *currentBlockData() const; VTextBlockData *previousBlockData() const; VTextBlockData *previousBlockData(const QTextBlock &p_block) const; void completeHighlight(QSharedPointer p_result); bool isMathJaxEnabled() const; void getFastParseBlockRange(int p_position, int p_charsRemoved, int p_charsAdded, int &p_firstBlock, int &p_lastBlock) const; void processFastParseResult(const QSharedPointer &p_result); bool highlightBlockOne(const QVector> &p_highlights, int p_blockNum, QVector *p_cache); void highlightBlockOne(const QVector &p_units); // To avoid line height jitter and code block mess. bool preHighlightSingleFormatBlock(const QVector> &p_highlights, int p_blockNum, const QString &p_text, bool p_forced); void updateSingleFormatBlocks(const QVector> &p_highlights); void rehighlightBlocks(); void rehighlightBlocksLater(); bool rehighlightBlockRange(int p_first, int p_last); TimeStamp nextCodeBlockTimeStamp(); bool isFastParseBlock(int p_blockNum) const; void clearFastParseResult(); static VTextBlockData *getBlockData(const QTextBlock &p_block); static bool isEmptyCodeBlockHighlights(const QVector> &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; TimeStamp m_codeBlockTimeStamp; QVector m_styles; QHash m_codeBlockStyles; QTextCharFormat m_codeBlockFormat; QTextCharFormat m_colorColumnFormat; PegParser *m_parser; QSharedPointer m_result; QSharedPointer m_fastResult; // Block range of fast parse, inclusive. QPair m_fastParseBlocks; // Block number of those blocks which possible contains previewed image. QSet m_possiblePreviewBlocks; // Extensions for parser. int m_parserExts; // Timer to trigger parse. QTimer *m_timer; int m_parseInterval; QTimer *m_fastParseTimer; QTimer *m_scrollRehighlightTimer; QTimer *m_rehighlightTimer; // Blocks have only one format set which occupies the whole block. QSet m_singleFormatBlocks; bool m_notifyHighlightComplete; // Time since last content change. QTime m_contentChangeTime; // Interval for fast parse timer. int m_fastParseInterval; }; inline const QVector &PegMarkdownHighlighter::getHeaderRegions() const { return m_result->m_headerRegions; } inline const QVector &PegMarkdownHighlighter::getImageRegions() const { return m_result->m_imageRegions; } inline const QSet &PegMarkdownHighlighter::getPossiblePreviewBlocks() const { return m_possiblePreviewBlocks; } inline void PegMarkdownHighlighter::clearPossiblePreviewBlocks(const QVector &p_blocksToClear) { for (auto i : p_blocksToClear) { m_possiblePreviewBlocks.remove(i); } } inline void PegMarkdownHighlighter::addPossiblePreviewBlock(int p_blockNumber) { m_possiblePreviewBlocks.insert(p_blockNumber); } inline QHash &PegMarkdownHighlighter::getCodeBlockStyles() { return m_codeBlockStyles; } inline QVector &PegMarkdownHighlighter::getStyles() { return m_styles; } inline const QVector &PegMarkdownHighlighter::getStyles() const { return m_styles; } inline const QTextDocument *PegMarkdownHighlighter::getDocument() const { return m_doc; } inline VTextBlockData *PegMarkdownHighlighter::currentBlockData() const { return static_cast(currentBlockUserData()); } inline VTextBlockData *PegMarkdownHighlighter::previousBlockData() const { QTextBlock block = currentBlock().previous(); if (!block.isValid()) { return NULL; } return static_cast(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(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(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(p_block.userData()); if (data) { data->setTimeStamp(p_ts); } } inline TimeStamp PegMarkdownHighlighter::blockCodeBlockTimeStamp(const QTextBlock &p_block) { VTextBlockData *data = static_cast(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(p_block.userData()); if (data) { data->setCodeBlockTimeStamp(p_ts); } } inline bool PegMarkdownHighlighter::isEmptyCodeBlockHighlights(const QVector> &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; } inline VTextBlockData *PegMarkdownHighlighter::getBlockData(const QTextBlock &p_block) { return static_cast(p_block.userData()); } inline TimeStamp PegMarkdownHighlighter::nextCodeBlockTimeStamp() { return ++m_codeBlockTimeStamp; } inline bool PegMarkdownHighlighter::isFastParseBlock(int p_blockNum) const { return p_blockNum >= m_fastParseBlocks.first && p_blockNum <= m_fastParseBlocks.second; } inline const QVector &PegMarkdownHighlighter::getCodeBlocks() const { return m_result->m_codeBlocks; } #endif // PEGMARKDOWNHIGHLIGHTER_H