| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- /* PEG Markdown Highlight
- * Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
- * Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
- *
- * highlighter.cpp
- *
- * Qt 4.7 example for highlighting a rich text widget.
- */
- #include <QtGui>
- #include <QtDebug>
- #include "hgmarkdownhighlighter.h"
- const int HGMarkdownHighlighter::initCapacity = 1024;
- void HGMarkdownHighlighter::resizeBuffer(int newCap)
- {
- if (newCap == capacity) {
- return;
- }
- if (capacity > 0) {
- Q_ASSERT(content);
- delete [] content;
- }
- capacity = newCap;
- content = new char [capacity];
- }
- // Will be freeed by parent automatically
- HGMarkdownHighlighter::HGMarkdownHighlighter(const QVector<HighlightingStyle> &styles, int waitInterval,
- QTextDocument *parent)
- : QSyntaxHighlighter(parent), parsing(0),
- waitInterval(waitInterval), content(NULL), capacity(0), result(NULL)
- {
- codeBlockStartExp = QRegExp("^(\\s)*```");
- codeBlockEndExp = QRegExp("^(\\s)*```$");
- codeBlockFormat.setForeground(QBrush(Qt::darkYellow));
- for (int index = 0; index < styles.size(); ++index) {
- if (styles[index].type == pmh_VERBATIM) {
- codeBlockFormat = styles[index].format;
- break;
- }
- }
- resizeBuffer(initCapacity);
- setStyles(styles);
- document = parent;
- timer = new QTimer(this);
- timer->setSingleShot(true);
- timer->setInterval(this->waitInterval);
- connect(timer, &QTimer::timeout, this, &HGMarkdownHighlighter::timerTimeout);
- connect(document, &QTextDocument::contentsChange,
- this, &HGMarkdownHighlighter::handleContentChange);
- }
- HGMarkdownHighlighter::~HGMarkdownHighlighter()
- {
- if (result) {
- pmh_free_elements(result);
- result = NULL;
- }
- if (content) {
- delete [] content;
- capacity = 0;
- content = NULL;
- }
- }
- void HGMarkdownHighlighter::highlightBlock(const QString &text)
- {
- int blockNum = currentBlock().blockNumber();
- if (!parsing && blockHighlights.size() > blockNum) {
- QVector<HLUnit> &units = blockHighlights[blockNum];
- for (int i = 0; i < units.size(); ++i) {
- // TODO: merge two format within the same range
- const HLUnit& unit = units[i];
- setFormat(unit.start, unit.length, highlightingStyles[unit.styleIndex].format);
- }
- }
- setCurrentBlockState(0);
- highlightCodeBlock(text);
- }
- void HGMarkdownHighlighter::setStyles(const QVector<HighlightingStyle> &styles)
- {
- this->highlightingStyles = styles;
- }
- void HGMarkdownHighlighter::initBlockHighlightFromResult(int nrBlocks)
- {
- blockHighlights.resize(nrBlocks);
- for (int i = 0; i < blockHighlights.size(); ++i) {
- blockHighlights[i].clear();
- }
- if (!result) {
- return;
- }
- for (int i = 0; i < highlightingStyles.size(); i++)
- {
- const HighlightingStyle &style = highlightingStyles[i];
- pmh_element *elem_cursor = result[style.type];
- while (elem_cursor != NULL)
- {
- if (elem_cursor->end <= elem_cursor->pos) {
- elem_cursor = elem_cursor->next;
- continue;
- }
- initBlockHighlihgtOne(elem_cursor->pos, elem_cursor->end, i);
- elem_cursor = elem_cursor->next;
- }
- }
- pmh_free_elements(result);
- result = NULL;
- }
- void HGMarkdownHighlighter::initBlockHighlihgtOne(unsigned long pos, unsigned long end, int styleIndex)
- {
- int startBlockNum = document->findBlock(pos).blockNumber();
- int endBlockNum = document->findBlock(end).blockNumber();
- for (int i = startBlockNum; i <= endBlockNum; ++i)
- {
- QTextBlock block = document->findBlockByNumber(i);
- int blockStartPos = block.position();
- HLUnit unit;
- if (i == startBlockNum) {
- unit.start = pos - blockStartPos;
- unit.length = (startBlockNum == endBlockNum) ?
- (end - pos) : (block.length() - unit.start);
- } else if (i == endBlockNum) {
- unit.start = 0;
- unit.length = end - blockStartPos;
- } else {
- unit.start = 0;
- unit.length = block.length();
- }
- unit.styleIndex = styleIndex;
- blockHighlights[i].append(unit);
- }
- }
- void HGMarkdownHighlighter::highlightCodeBlock(const QString &text)
- {
- int nextIndex = 0;
- int startIndex = 0;
- if (previousBlockState() != 1) {
- startIndex = codeBlockStartExp.indexIn(text);
- if (startIndex >= 0) {
- nextIndex = startIndex + codeBlockStartExp.matchedLength();
- } else {
- nextIndex = -1;
- }
- }
- while (nextIndex >= 0) {
- int endIndex = codeBlockEndExp.indexIn(text, nextIndex);
- int codeBlockLength;
- if (endIndex == -1) {
- setCurrentBlockState(1);
- codeBlockLength = text.length() - startIndex;
- } else {
- codeBlockLength = endIndex - startIndex + codeBlockEndExp.matchedLength();
- }
- setFormat(startIndex, codeBlockLength, codeBlockFormat);
- startIndex = codeBlockStartExp.indexIn(text, startIndex + codeBlockLength);
- if (startIndex >= 0) {
- nextIndex = startIndex + codeBlockStartExp.matchedLength();
- } else {
- nextIndex = -1;
- }
- }
- }
- void HGMarkdownHighlighter::parse()
- {
- if (!parsing.testAndSetRelaxed(0, 1)) {
- return;
- }
- int nrBlocks = document->blockCount();
- parseInternal();
- if (highlightingStyles.isEmpty()) {
- qWarning() << "error: HighlightingStyles is not set";
- return;
- }
- initBlockHighlightFromResult(nrBlocks);
- parsing.store(0);
- }
- void HGMarkdownHighlighter::parseInternal()
- {
- QString text = document->toPlainText();
- QByteArray ba = text.toUtf8();
- const char *data = (const char *)ba.data();
- int len = ba.size();
- if (result) {
- pmh_free_elements(result);
- result = NULL;
- }
- if (len == 0) {
- return;
- } else if (len >= capacity) {
- resizeBuffer(qMax(2 * capacity, len * 2));
- } else if (len < (capacity >> 2)) {
- resizeBuffer(qMax(capacity >> 1, len * 2));
- }
- memcpy(content, data, len);
- content[len] = '\0';
- pmh_markdown_to_elements(content, pmh_EXT_NONE, &result);
- }
- void HGMarkdownHighlighter::handleContentChange(int position, int charsRemoved, int charsAdded)
- {
- if (charsRemoved == 0 && charsAdded == 0) {
- return;
- }
- timer->stop();
- timer->start();
- }
- void HGMarkdownHighlighter::timerTimeout()
- {
- parse();
- rehighlight();
- }
|