hgmarkdownhighlighter.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. #ifndef HGMARKDOWNHIGHLIGHTER_H
  2. #define HGMARKDOWNHIGHLIGHTER_H
  3. #include <QTextCharFormat>
  4. #include <QSyntaxHighlighter>
  5. #include <QAtomicInt>
  6. #include <QMap>
  7. #include <QString>
  8. #include <QHash>
  9. extern "C" {
  10. #include <pmh_parser.h>
  11. }
  12. QT_BEGIN_NAMESPACE
  13. class QTextDocument;
  14. QT_END_NAMESPACE
  15. struct HighlightingStyle
  16. {
  17. pmh_element_type type;
  18. QTextCharFormat format;
  19. };
  20. enum HighlightBlockState
  21. {
  22. Normal = 0,
  23. // A fenced code block.
  24. CodeBlockStart,
  25. CodeBlock,
  26. CodeBlockEnd,
  27. // This block is inside a HTML comment region.
  28. Comment
  29. };
  30. // One continuous region for a certain markdown highlight style
  31. // within a QTextBlock.
  32. // Pay attention to the change of HighlightingStyles[]
  33. struct HLUnit
  34. {
  35. // Highlight offset @start and @length with style HighlightingStyles[styleIndex]
  36. // within a QTextBlock
  37. unsigned long start;
  38. unsigned long length;
  39. unsigned int styleIndex;
  40. };
  41. struct HLUnitStyle
  42. {
  43. unsigned long start;
  44. unsigned long length;
  45. QString style;
  46. };
  47. // Fenced code block only.
  48. struct VCodeBlock
  49. {
  50. int m_startPos;
  51. int m_startBlock;
  52. int m_endBlock;
  53. QString m_lang;
  54. QString m_text;
  55. };
  56. // Highlight unit with global position and string style name.
  57. struct HLUnitPos
  58. {
  59. HLUnitPos() : m_position(-1), m_length(-1)
  60. {
  61. }
  62. HLUnitPos(int p_position, int p_length, const QString &p_style)
  63. : m_position(p_position), m_length(p_length), m_style(p_style)
  64. {
  65. }
  66. int m_position;
  67. int m_length;
  68. QString m_style;
  69. };
  70. // Denote the region of a certain Markdown element.
  71. struct VElementRegion
  72. {
  73. VElementRegion() : m_startPos(0), m_endPos(0) {}
  74. VElementRegion(int p_start, int p_end) : m_startPos(p_start), m_endPos(p_end) {}
  75. // The start position of the region in document.
  76. int m_startPos;
  77. // The end position of the region in document.
  78. int m_endPos;
  79. // Whether this region contains @p_pos.
  80. bool contains(int p_pos) const
  81. {
  82. return m_startPos <= p_pos && m_endPos >= p_pos;
  83. }
  84. bool operator==(const VElementRegion &p_other) const
  85. {
  86. return (m_startPos == p_other.m_startPos
  87. && m_endPos == p_other.m_endPos);
  88. }
  89. bool operator<(const VElementRegion &p_other) const
  90. {
  91. if (m_startPos < p_other.m_startPos) {
  92. return true;
  93. } else if (m_startPos == p_other.m_startPos) {
  94. return m_endPos <= p_other.m_endPos;
  95. } else {
  96. return false;
  97. }
  98. }
  99. };
  100. class HGMarkdownHighlighter : public QSyntaxHighlighter
  101. {
  102. Q_OBJECT
  103. public:
  104. HGMarkdownHighlighter(const QVector<HighlightingStyle> &styles,
  105. const QHash<QString, QTextCharFormat> &codeBlockStyles,
  106. int waitInterval,
  107. QTextDocument *parent = 0);
  108. ~HGMarkdownHighlighter();
  109. // Request to update highlihgt (re-parse and re-highlight)
  110. void setCodeBlockHighlights(const QVector<HLUnitPos> &p_units);
  111. const QMap<int, bool> &getPotentialPreviewBlocks() const;
  112. signals:
  113. void highlightCompleted();
  114. // QVector is implicitly shared.
  115. void codeBlocksUpdated(const QVector<VCodeBlock> &p_codeBlocks);
  116. // Emitted when image regions have been fetched from a new parsing result.
  117. void imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions);
  118. // Emitted when header regions have been fetched from a new parsing result.
  119. void headersUpdated(const QVector<VElementRegion> &p_headerRegions);
  120. protected:
  121. void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
  122. public slots:
  123. void updateHighlight();
  124. private slots:
  125. void handleContentChange(int position, int charsRemoved, int charsAdded);
  126. void timerTimeout();
  127. private:
  128. QRegExp codeBlockStartExp;
  129. QRegExp codeBlockEndExp;
  130. QTextCharFormat codeBlockFormat;
  131. QTextCharFormat m_linkFormat;
  132. QTextCharFormat m_imageFormat;
  133. QTextDocument *document;
  134. QVector<HighlightingStyle> highlightingStyles;
  135. QHash<QString, QTextCharFormat> m_codeBlockStyles;
  136. QVector<QVector<HLUnit> > blockHighlights;
  137. // Use another member to store the codeblocks highlights, because the highlight
  138. // sequence is blockHighlights, regular-expression-based highlihgts, and then
  139. // codeBlockHighlights.
  140. // Support fenced code block only.
  141. QVector<QVector<HLUnitStyle> > m_codeBlockHighlights;
  142. int m_numOfCodeBlockHighlightsToRecv;
  143. // If the ith block contains preview, then the set contains i.
  144. // If the set contains i, the ith block may contain preview.
  145. // We just use the key.
  146. QMap<int, bool> m_potentialPreviewBlocks;
  147. // All HTML comment regions.
  148. QVector<VElementRegion> m_commentRegions;
  149. // All image link regions.
  150. QVector<VElementRegion> m_imageRegions;
  151. // All header regions.
  152. // May contains illegal elements.
  153. // Sorted by start position.
  154. QVector<VElementRegion> m_headerRegions;
  155. // Timer to signal highlightCompleted().
  156. QTimer *m_completeTimer;
  157. QAtomicInt parsing;
  158. QTimer *timer;
  159. int waitInterval;
  160. char *content;
  161. int capacity;
  162. pmh_element **result;
  163. static const int initCapacity;
  164. void resizeBuffer(int newCap);
  165. void highlightCodeBlock(const QString &text);
  166. // Highlight links using regular expression.
  167. // PEG Markdown Highlight treat URLs with spaces illegal. This function is
  168. // intended to complement this.
  169. void highlightLinkWithSpacesInURL(const QString &p_text);
  170. void parse();
  171. void parseInternal();
  172. void initBlockHighlightFromResult(int nrBlocks);
  173. void initBlockHighlihgtOne(unsigned long pos, unsigned long end,
  174. int styleIndex);
  175. // Return true if there are fenced code blocks and it will call rehighlight() later.
  176. // Return false if there is none.
  177. bool updateCodeBlocks();
  178. // Fetch all the HTML comment regions from parsing result.
  179. void initHtmlCommentRegionsFromResult();
  180. // Fetch all the image link regions from parsing result.
  181. void initImageRegionsFromResult();
  182. // Fetch all the header regions from parsing result.
  183. void initHeaderRegionsFromResult();
  184. // Whether @p_block is totally inside a HTML comment.
  185. bool isBlockInsideCommentRegion(const QTextBlock &p_block) const;
  186. // Highlights have been changed. Try to signal highlightCompleted().
  187. void highlightChanged();
  188. // Set the user data of currentBlock().
  189. void updateBlockUserData(int p_blockNum, const QString &p_text);
  190. };
  191. inline const QMap<int, bool> &HGMarkdownHighlighter::getPotentialPreviewBlocks() const
  192. {
  193. return m_potentialPreviewBlocks;
  194. }
  195. #endif