#ifndef PEGPARSER_H #define PEGPARSER_H #include #include #include #include #include #include "vconstants.h" #include "markdownhighlighterdata.h" struct PegParseConfig { PegParseConfig() : m_timeStamp(0), m_numOfBlocks(0), m_offset(0), m_extensions(pmh_EXT_NONE), m_fast(false) { } TimeStamp m_timeStamp; QByteArray m_data; int m_numOfBlocks; // Offset of m_data in the document. int m_offset; int m_extensions; // Fast parse. bool m_fast; QString toString() const { return QString("PegParseConfig ts %1 data %2 blocks %3").arg(m_timeStamp) .arg(m_data.size()) .arg(m_numOfBlocks); } }; struct PegParseResult { PegParseResult(const QSharedPointer &p_config) : m_timeStamp(p_config->m_timeStamp), m_numOfBlocks(p_config->m_numOfBlocks), m_offset(p_config->m_offset), m_pmhElements(NULL) { } ~PegParseResult() { clearPmhElements(); } void clearPmhElements() { if (m_pmhElements) { pmh_free_elements(m_pmhElements); m_pmhElements = NULL; } } bool operator<(const PegParseResult &p_other) const { return m_timeStamp < p_other.m_timeStamp; } QString toString() const { return QString("PegParseResult ts %1").arg(m_timeStamp); } bool isEmpty() const { return !m_pmhElements; } // Parse m_pmhElements. void parse(QAtomicInt &p_stop, bool p_fast); TimeStamp m_timeStamp; int m_numOfBlocks; int m_offset; pmh_element **m_pmhElements; // All image link regions. QVector m_imageRegions; // All header regions. // Sorted by start position. QVector m_headerRegions; // Fenced code block regions. // Ordered by start position in ascending order. QMap m_codeBlockRegions; // All $ $ inline equation regions. QVector m_inlineEquationRegions; // All $$ $$ display formula regions. // Sorted by start position. QVector m_displayFormulaRegions; // HRule regions. QVector m_hruleRegions; private: void parseImageRegions(QAtomicInt &p_stop); void parseHeaderRegions(QAtomicInt &p_stop); void parseFencedCodeBlockRegions(QAtomicInt &p_stop); void parseInlineEquationRegions(QAtomicInt &p_stop); void parseDisplayFormulaRegions(QAtomicInt &p_stop); void parseHRuleRegions(QAtomicInt &p_stop); }; class PegParserWorker : public QThread { Q_OBJECT public: explicit PegParserWorker(QObject *p_parent = nullptr); void prepareParse(const QSharedPointer &p_config); void reset(); int state() const { return m_state; } TimeStamp workTimeStamp() const { if (m_parseConfig.isNull()) { return 0; } return m_parseConfig->m_timeStamp; } const QSharedPointer &parseConfig() const { return m_parseConfig; } const QSharedPointer &parseResult() const { return m_parseResult; } public slots: void stop(); protected: void run() Q_DECL_OVERRIDE; private: QSharedPointer parseMarkdown(const QSharedPointer &p_config, QAtomicInt &p_stop); bool isAskedToStop() const { return m_stop.load() == 1; } QAtomicInt m_stop; int m_state; QSharedPointer m_parseConfig; QSharedPointer m_parseResult; }; class PegParser : public QObject { Q_OBJECT public: explicit PegParser(QObject *p_parent = nullptr); ~PegParser(); QSharedPointer parse(const QSharedPointer &p_config); void parseAsync(const QSharedPointer &p_config); static QVector parseImageRegions(const QSharedPointer &p_config); // MUST pmh_free_elements() the result. static pmh_element **parseMarkdownToElements(const QSharedPointer &p_config); signals: void parseResultReady(const QSharedPointer &p_result); private slots: void handleWorkerFinished(PegParserWorker *p_worker); private: void init(); void clear(); void pickWorker(); void scheduleWork(PegParserWorker *p_worker, const QSharedPointer &p_config); // Maintain a fixed number of workers to pick work. QVector m_workers; QSharedPointer m_pendingWork; }; #endif // PEGPARSER_H