mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
PegMarkdownHighlighter: multi-threads highlighter support
This commit is contained in:
parent
fbfc6c1dd6
commit
bb308a06d1
@ -29,6 +29,7 @@ HGMarkdownHighlighter::HGMarkdownHighlighter(const QVector<HighlightingStyle> &s
|
|||||||
int waitInterval,
|
int waitInterval,
|
||||||
QTextDocument *parent)
|
QTextDocument *parent)
|
||||||
: QSyntaxHighlighter(parent),
|
: QSyntaxHighlighter(parent),
|
||||||
|
m_timeStamp(0),
|
||||||
highlightingStyles(styles),
|
highlightingStyles(styles),
|
||||||
m_codeBlockStyles(codeBlockStyles),
|
m_codeBlockStyles(codeBlockStyles),
|
||||||
m_numOfCodeBlockHighlightsToRecv(0),
|
m_numOfCodeBlockHighlightsToRecv(0),
|
||||||
@ -864,9 +865,9 @@ void HGMarkdownHighlighter::handleContentChange(int /* position */, int charsRem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_signalOut = false;
|
++m_timeStamp;
|
||||||
|
|
||||||
timer->stop();
|
m_signalOut = false;
|
||||||
timer->start();
|
timer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#ifndef HGMARKDOWNHIGHLIGHTER_H
|
#ifndef HGMARKDOWNHIGHLIGHTER_H
|
||||||
#define HGMARKDOWNHIGHLIGHTER_H
|
#define HGMARKDOWNHIGHLIGHTER_H
|
||||||
|
|
||||||
#include <QTextCharFormat>
|
|
||||||
#include <QSyntaxHighlighter>
|
#include <QSyntaxHighlighter>
|
||||||
#include <QAtomicInt>
|
#include <QAtomicInt>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
@ -9,177 +8,10 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "vtextblockdata.h"
|
#include "vtextblockdata.h"
|
||||||
#include "vconstants.h"
|
#include "markdownhighlighterdata.h"
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <pmh_parser.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QTextDocument;
|
class QTextDocument;
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
struct HighlightingStyle
|
|
||||||
{
|
|
||||||
pmh_element_type type;
|
|
||||||
QTextCharFormat format;
|
|
||||||
};
|
|
||||||
|
|
||||||
// One continuous region for a certain markdown highlight style
|
|
||||||
// within a QTextBlock.
|
|
||||||
// Pay attention to the change of HighlightingStyles[]
|
|
||||||
struct HLUnit
|
|
||||||
{
|
|
||||||
// Highlight offset @start and @length with style HighlightingStyles[styleIndex]
|
|
||||||
// within a QTextBlock
|
|
||||||
unsigned long start;
|
|
||||||
unsigned long length;
|
|
||||||
unsigned int styleIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HLUnitStyle
|
|
||||||
{
|
|
||||||
unsigned long start;
|
|
||||||
unsigned long length;
|
|
||||||
QString style;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fenced code block only.
|
|
||||||
struct VCodeBlock
|
|
||||||
{
|
|
||||||
// Global position of the start.
|
|
||||||
int m_startPos;
|
|
||||||
|
|
||||||
int m_startBlock;
|
|
||||||
int m_endBlock;
|
|
||||||
|
|
||||||
QString m_lang;
|
|
||||||
|
|
||||||
QString m_text;
|
|
||||||
|
|
||||||
bool equalContent(const VCodeBlock &p_block) const
|
|
||||||
{
|
|
||||||
return p_block.m_lang == m_lang && p_block.m_text == m_text;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateNonContent(const VCodeBlock &p_block)
|
|
||||||
{
|
|
||||||
m_startPos = p_block.m_startPos;
|
|
||||||
m_startBlock = p_block.m_startBlock;
|
|
||||||
m_endBlock = p_block.m_endBlock;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct VMathjaxBlock
|
|
||||||
{
|
|
||||||
VMathjaxBlock()
|
|
||||||
: m_blockNumber(-1),
|
|
||||||
m_previewedAsBlock(false),
|
|
||||||
m_index(-1),
|
|
||||||
m_length(-1)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
VMathjaxBlock(int p_blockNumber, const MathjaxInfo &p_info)
|
|
||||||
: m_blockNumber(p_blockNumber),
|
|
||||||
m_previewedAsBlock(p_info.m_previewedAsBlock),
|
|
||||||
m_index(p_info.m_index),
|
|
||||||
m_length(p_info.m_length),
|
|
||||||
m_text(p_info.m_text)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool equalContent(const VMathjaxBlock &p_block) const
|
|
||||||
{
|
|
||||||
return m_text == p_block.m_text;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateNonContent(const VMathjaxBlock &p_block)
|
|
||||||
{
|
|
||||||
m_blockNumber = p_block.m_blockNumber;
|
|
||||||
m_previewedAsBlock = p_block.m_previewedAsBlock;
|
|
||||||
m_index = p_block.m_index;
|
|
||||||
m_length = p_block.m_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int m_blockNumber;
|
|
||||||
|
|
||||||
bool m_previewedAsBlock;
|
|
||||||
|
|
||||||
// Start index within the block.
|
|
||||||
int m_index;
|
|
||||||
|
|
||||||
int m_length;
|
|
||||||
|
|
||||||
QString m_text;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Highlight unit with global position and string style name.
|
|
||||||
struct HLUnitPos
|
|
||||||
{
|
|
||||||
HLUnitPos() : m_position(-1), m_length(-1)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
HLUnitPos(int p_position, int p_length, const QString &p_style)
|
|
||||||
: m_position(p_position), m_length(p_length), m_style(p_style)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int m_position;
|
|
||||||
int m_length;
|
|
||||||
QString m_style;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Denote the region of a certain Markdown element.
|
|
||||||
struct VElementRegion
|
|
||||||
{
|
|
||||||
VElementRegion() : m_startPos(0), m_endPos(0) {}
|
|
||||||
|
|
||||||
VElementRegion(int p_start, int p_end) : m_startPos(p_start), m_endPos(p_end) {}
|
|
||||||
|
|
||||||
// The start position of the region in document.
|
|
||||||
int m_startPos;
|
|
||||||
|
|
||||||
// The end position of the region in document.
|
|
||||||
int m_endPos;
|
|
||||||
|
|
||||||
// Whether this region contains @p_pos.
|
|
||||||
bool contains(int p_pos) const
|
|
||||||
{
|
|
||||||
return m_startPos <= p_pos && m_endPos > p_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool intersect(int p_start, int p_end) const
|
|
||||||
{
|
|
||||||
return !(p_end <= m_startPos || p_start >= m_endPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const VElementRegion &p_other) const
|
|
||||||
{
|
|
||||||
return (m_startPos == p_other.m_startPos
|
|
||||||
&& m_endPos == p_other.m_endPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator<(const VElementRegion &p_other) const
|
|
||||||
{
|
|
||||||
if (m_startPos < p_other.m_startPos) {
|
|
||||||
return true;
|
|
||||||
} else if (m_startPos == p_other.m_startPos) {
|
|
||||||
// If a < b is true, then b < a must be false.
|
|
||||||
return m_endPos < p_other.m_endPos;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString toString() const
|
|
||||||
{
|
|
||||||
return QString("[%1,%2)").arg(m_startPos).arg(m_endPos);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class HGMarkdownHighlighter : public QSyntaxHighlighter
|
class HGMarkdownHighlighter : public QSyntaxHighlighter
|
||||||
{
|
{
|
||||||
@ -195,8 +27,6 @@ public:
|
|||||||
// 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;
|
|
||||||
|
|
||||||
const QVector<VElementRegion> &getHeaderRegions() const;
|
const QVector<VElementRegion> &getHeaderRegions() const;
|
||||||
|
|
||||||
const QSet<int> &getPossiblePreviewBlocks() const;
|
const QSet<int> &getPossiblePreviewBlocks() const;
|
||||||
@ -259,6 +89,8 @@ private:
|
|||||||
int m_length;
|
int m_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TimeStamp m_timeStamp;
|
||||||
|
|
||||||
QRegExp codeBlockStartExp;
|
QRegExp codeBlockStartExp;
|
||||||
QRegExp codeBlockEndExp;
|
QRegExp codeBlockEndExp;
|
||||||
|
|
||||||
|
@ -1,4 +1,182 @@
|
|||||||
#ifndef MARKDOWNHIGHLIGHTERDATA_H
|
#ifndef MARKDOWNHIGHLIGHTERDATA_H
|
||||||
#define MARKDOWNHIGHLIGHTERDATA_H
|
#define MARKDOWNHIGHLIGHTERDATA_H
|
||||||
|
|
||||||
|
#include <QTextCharFormat>
|
||||||
|
|
||||||
|
#include "vconstants.h"
|
||||||
|
#include "vtextblockdata.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <pmh_parser.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HighlightingStyle
|
||||||
|
{
|
||||||
|
pmh_element_type type;
|
||||||
|
QTextCharFormat format;
|
||||||
|
};
|
||||||
|
|
||||||
|
// One continuous region for a certain markdown highlight style
|
||||||
|
// within a QTextBlock.
|
||||||
|
// Pay attention to the change of HighlightingStyles[]
|
||||||
|
struct HLUnit
|
||||||
|
{
|
||||||
|
// Highlight offset @start and @length with style HighlightingStyles[styleIndex]
|
||||||
|
// within a QTextBlock
|
||||||
|
unsigned long start;
|
||||||
|
unsigned long length;
|
||||||
|
unsigned int styleIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HLUnitStyle
|
||||||
|
{
|
||||||
|
unsigned long start;
|
||||||
|
unsigned long length;
|
||||||
|
QString style;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fenced code block only.
|
||||||
|
struct VCodeBlock
|
||||||
|
{
|
||||||
|
// Global position of the start.
|
||||||
|
int m_startPos;
|
||||||
|
|
||||||
|
int m_startBlock;
|
||||||
|
int m_endBlock;
|
||||||
|
|
||||||
|
QString m_lang;
|
||||||
|
|
||||||
|
QString m_text;
|
||||||
|
|
||||||
|
bool equalContent(const VCodeBlock &p_block) const
|
||||||
|
{
|
||||||
|
return p_block.m_lang == m_lang && p_block.m_text == m_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateNonContent(const VCodeBlock &p_block)
|
||||||
|
{
|
||||||
|
m_startPos = p_block.m_startPos;
|
||||||
|
m_startBlock = p_block.m_startBlock;
|
||||||
|
m_endBlock = p_block.m_endBlock;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct VMathjaxBlock
|
||||||
|
{
|
||||||
|
VMathjaxBlock()
|
||||||
|
: m_blockNumber(-1),
|
||||||
|
m_previewedAsBlock(false),
|
||||||
|
m_index(-1),
|
||||||
|
m_length(-1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VMathjaxBlock(int p_blockNumber, const MathjaxInfo &p_info)
|
||||||
|
: m_blockNumber(p_blockNumber),
|
||||||
|
m_previewedAsBlock(p_info.m_previewedAsBlock),
|
||||||
|
m_index(p_info.m_index),
|
||||||
|
m_length(p_info.m_length),
|
||||||
|
m_text(p_info.m_text)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool equalContent(const VMathjaxBlock &p_block) const
|
||||||
|
{
|
||||||
|
return m_text == p_block.m_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateNonContent(const VMathjaxBlock &p_block)
|
||||||
|
{
|
||||||
|
m_blockNumber = p_block.m_blockNumber;
|
||||||
|
m_previewedAsBlock = p_block.m_previewedAsBlock;
|
||||||
|
m_index = p_block.m_index;
|
||||||
|
m_length = p_block.m_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_blockNumber;
|
||||||
|
|
||||||
|
bool m_previewedAsBlock;
|
||||||
|
|
||||||
|
// Start index within the block.
|
||||||
|
int m_index;
|
||||||
|
|
||||||
|
int m_length;
|
||||||
|
|
||||||
|
QString m_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Highlight unit with global position and string style name.
|
||||||
|
struct HLUnitPos
|
||||||
|
{
|
||||||
|
HLUnitPos() : m_position(-1), m_length(-1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
HLUnitPos(int p_position, int p_length, const QString &p_style)
|
||||||
|
: m_position(p_position), m_length(p_length), m_style(p_style)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_position;
|
||||||
|
int m_length;
|
||||||
|
QString m_style;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Denote the region of a certain Markdown element.
|
||||||
|
struct VElementRegion
|
||||||
|
{
|
||||||
|
VElementRegion() : m_startPos(0), m_endPos(0) {}
|
||||||
|
|
||||||
|
VElementRegion(int p_start, int p_end) : m_startPos(p_start), m_endPos(p_end) {}
|
||||||
|
|
||||||
|
// The start position of the region in document.
|
||||||
|
int m_startPos;
|
||||||
|
|
||||||
|
// The end position of the region in document.
|
||||||
|
int m_endPos;
|
||||||
|
|
||||||
|
// Whether this region contains @p_pos.
|
||||||
|
bool contains(int p_pos) const
|
||||||
|
{
|
||||||
|
return m_startPos <= p_pos && m_endPos > p_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool intersect(int p_start, int p_end) const
|
||||||
|
{
|
||||||
|
return !(p_end <= m_startPos || p_start >= m_endPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const VElementRegion &p_other) const
|
||||||
|
{
|
||||||
|
return (m_startPos == p_other.m_startPos
|
||||||
|
&& m_endPos == p_other.m_endPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const VElementRegion &p_other) const
|
||||||
|
{
|
||||||
|
if (m_startPos < p_other.m_startPos) {
|
||||||
|
return true;
|
||||||
|
} else if (m_startPos == p_other.m_startPos) {
|
||||||
|
// If a < b is true, then b < a must be false.
|
||||||
|
return m_endPos < p_other.m_endPos;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString toString() const
|
||||||
|
{
|
||||||
|
return QString("[%1,%2)").arg(m_startPos).arg(m_endPos);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PegHighlightResult
|
||||||
|
{
|
||||||
|
TimeStamp m_timeStamp;
|
||||||
|
|
||||||
|
QVector<QVector<HLUnit> > m_blockHighlights;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // MARKDOWNHIGHLIGHTERDATA_H
|
#endif // MARKDOWNHIGHLIGHTERDATA_H
|
||||||
|
223
src/peghighlighterresult.cpp
Normal file
223
src/peghighlighterresult.cpp
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#include "peghighlighterresult.h"
|
||||||
|
|
||||||
|
#include <QTextDocument>
|
||||||
|
#include <QTextBlock>
|
||||||
|
|
||||||
|
#include "pegmarkdownhighlighter.h"
|
||||||
|
#include "utils/vutils.h"
|
||||||
|
|
||||||
|
PegHighlighterResult::PegHighlighterResult()
|
||||||
|
: m_timeStamp(0),
|
||||||
|
m_numOfBlocks(0),
|
||||||
|
m_numOfCodeBlockHighlightsToRecv(0)
|
||||||
|
{
|
||||||
|
m_codeBlockStartExp = QRegExp(VUtils::c_fencedCodeBlockStartRegExp);
|
||||||
|
m_codeBlockEndExp = QRegExp(VUtils::c_fencedCodeBlockEndRegExp);
|
||||||
|
}
|
||||||
|
|
||||||
|
PegHighlighterResult::PegHighlighterResult(const PegMarkdownHighlighter *p_peg,
|
||||||
|
const QSharedPointer<PegParseResult> &p_result)
|
||||||
|
: m_timeStamp(p_result->m_timeStamp),
|
||||||
|
m_numOfBlocks(p_result->m_numOfBlocks),
|
||||||
|
m_numOfCodeBlockHighlightsToRecv(0)
|
||||||
|
{
|
||||||
|
m_codeBlockStartExp = QRegExp(VUtils::c_fencedCodeBlockStartRegExp);
|
||||||
|
m_codeBlockEndExp = QRegExp(VUtils::c_fencedCodeBlockEndRegExp);
|
||||||
|
|
||||||
|
parseBlocksHighlights(p_peg, p_result);
|
||||||
|
|
||||||
|
// Implicit sharing.
|
||||||
|
m_imageRegions = p_result->m_imageRegions;
|
||||||
|
m_headerRegions = p_result->m_headerRegions;
|
||||||
|
|
||||||
|
parseFencedCodeBlocks(p_peg, p_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool compHLUnit(const HLUnit &p_a, const HLUnit &p_b)
|
||||||
|
{
|
||||||
|
if (p_a.start < p_b.start) {
|
||||||
|
return true;
|
||||||
|
} else if (p_a.start == p_b.start) {
|
||||||
|
return p_a.length > p_b.length;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegHighlighterResult::parseBlocksHighlights(const PegMarkdownHighlighter *p_peg,
|
||||||
|
const QSharedPointer<PegParseResult> &p_result)
|
||||||
|
{
|
||||||
|
m_blocksHighlights.resize(m_numOfBlocks);
|
||||||
|
if (p_result->isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QTextDocument *doc = p_peg->getDocument();
|
||||||
|
const QVector<HighlightingStyle> &styles = p_peg->getStyles();
|
||||||
|
auto pmhResult = p_result->m_pmhElements;
|
||||||
|
for (int i = 0; i < styles.size(); i++)
|
||||||
|
{
|
||||||
|
const HighlightingStyle &style = styles[i];
|
||||||
|
pmh_element *elem_cursor = pmhResult[style.type];
|
||||||
|
while (elem_cursor != NULL)
|
||||||
|
{
|
||||||
|
// elem_cursor->pos and elem_cursor->end is the start
|
||||||
|
// and end position of the element in document.
|
||||||
|
if (elem_cursor->end <= elem_cursor->pos) {
|
||||||
|
elem_cursor = elem_cursor->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseBlocksHighlightOne(doc, elem_cursor->pos, elem_cursor->end, i);
|
||||||
|
elem_cursor = elem_cursor->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort m_blocksHighlights.
|
||||||
|
for (int i = 0; i < m_blocksHighlights.size(); ++i) {
|
||||||
|
if (m_blocksHighlights[i].size() > 1) {
|
||||||
|
std::sort(m_blocksHighlights[i].begin(), m_blocksHighlights[i].end(), compHLUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegHighlighterResult::parseBlocksHighlightOne(const QTextDocument *p_doc,
|
||||||
|
unsigned long p_pos,
|
||||||
|
unsigned long p_end,
|
||||||
|
int p_styleIndex)
|
||||||
|
{
|
||||||
|
// When the the highlight element is at the end of document, @p_end will equals
|
||||||
|
// to the characterCount.
|
||||||
|
unsigned int nrChar = (unsigned int)p_doc->characterCount();
|
||||||
|
if (p_end >= nrChar && nrChar > 0) {
|
||||||
|
p_end = nrChar - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextBlock block = p_doc->findBlock(p_pos);
|
||||||
|
int startBlockNum = block.blockNumber();
|
||||||
|
int endBlockNum = p_doc->findBlock(p_end).blockNumber();
|
||||||
|
while (block.isValid())
|
||||||
|
{
|
||||||
|
int blockNum = block.blockNumber();
|
||||||
|
if (blockNum > endBlockNum) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockStartPos = block.position();
|
||||||
|
HLUnit unit;
|
||||||
|
if (blockNum == startBlockNum) {
|
||||||
|
unit.start = p_pos - blockStartPos;
|
||||||
|
unit.length = (startBlockNum == endBlockNum) ?
|
||||||
|
(p_end - p_pos) : (block.length() - unit.start);
|
||||||
|
} else if (blockNum == endBlockNum) {
|
||||||
|
unit.start = 0;
|
||||||
|
unit.length = p_end - blockStartPos;
|
||||||
|
} else {
|
||||||
|
unit.start = 0;
|
||||||
|
unit.length = block.length();
|
||||||
|
}
|
||||||
|
unit.styleIndex = p_styleIndex;
|
||||||
|
|
||||||
|
m_blocksHighlights[blockNum].append(unit);
|
||||||
|
|
||||||
|
block = block.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegHighlighterResult::parseBlocksElementRegionOne(QHash<int, QVector<VElementRegion>> &p_regs,
|
||||||
|
const QTextDocument *p_doc,
|
||||||
|
unsigned long p_pos,
|
||||||
|
unsigned long p_end)
|
||||||
|
{
|
||||||
|
// When the the highlight element is at the end of document, @p_end will equals
|
||||||
|
// to the characterCount.
|
||||||
|
unsigned int nrChar = (unsigned int)p_doc->characterCount();
|
||||||
|
if (p_end >= nrChar && nrChar > 0) {
|
||||||
|
p_end = nrChar - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextBlock block = p_doc->findBlock(p_pos);
|
||||||
|
int startBlockNum = block.blockNumber();
|
||||||
|
int endBlockNum = p_doc->findBlock(p_end).blockNumber();
|
||||||
|
while (block.isValid())
|
||||||
|
{
|
||||||
|
int blockNum = block.blockNumber();
|
||||||
|
if (blockNum > endBlockNum) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockStartPos = block.position();
|
||||||
|
QVector<VElementRegion> ®s = p_regs[blockNum];
|
||||||
|
int start, end;
|
||||||
|
if (blockNum == startBlockNum) {
|
||||||
|
start = p_pos - blockStartPos;
|
||||||
|
end = (startBlockNum == endBlockNum) ? (p_end - blockStartPos)
|
||||||
|
: block.length();
|
||||||
|
} else if (blockNum == endBlockNum) {
|
||||||
|
start = 0;
|
||||||
|
end = p_end - blockStartPos;
|
||||||
|
} else {
|
||||||
|
start = 0;
|
||||||
|
end = block.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
regs.append(VElementRegion(start, end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegHighlighterResult::parseFencedCodeBlocks(const PegMarkdownHighlighter *p_peg,
|
||||||
|
const QSharedPointer<PegParseResult> &p_result)
|
||||||
|
{
|
||||||
|
const QMap<int, VElementRegion> ®s = p_result->m_codeBlockRegions;
|
||||||
|
|
||||||
|
const QTextDocument *doc = p_peg->getDocument();
|
||||||
|
VCodeBlock item;
|
||||||
|
bool inBlock = false;
|
||||||
|
for (auto it = regs.begin(); it != regs.end(); ++it) {
|
||||||
|
QTextBlock block = doc->findBlock(it.value().m_startPos);
|
||||||
|
int lastBlock = doc->findBlock(it.value().m_endPos - 1).blockNumber();
|
||||||
|
|
||||||
|
while (block.isValid()) {
|
||||||
|
int blockNumber = block.blockNumber();
|
||||||
|
if (blockNumber > lastBlock) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
HighlightBlockState state = HighlightBlockState::Normal;
|
||||||
|
QString text = block.text();
|
||||||
|
if (inBlock) {
|
||||||
|
item.m_text = item.m_text + "\n" + text;
|
||||||
|
int idx = m_codeBlockEndExp.indexIn(text);
|
||||||
|
if (idx >= 0) {
|
||||||
|
// End block.
|
||||||
|
inBlock = false;
|
||||||
|
state = HighlightBlockState::CodeBlockEnd;
|
||||||
|
item.m_endBlock = blockNumber;
|
||||||
|
m_codeBlocks.append(item);
|
||||||
|
} else {
|
||||||
|
// Within code block.
|
||||||
|
state = HighlightBlockState::CodeBlock;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int idx = m_codeBlockStartExp.indexIn(text);
|
||||||
|
if (idx >= 0) {
|
||||||
|
// Start block.
|
||||||
|
inBlock = true;
|
||||||
|
state = HighlightBlockState::CodeBlockStart;
|
||||||
|
item.m_startBlock = blockNumber;
|
||||||
|
item.m_startPos = block.position();
|
||||||
|
item.m_text = text;
|
||||||
|
if (m_codeBlockStartExp.captureCount() == 2) {
|
||||||
|
item.m_lang = m_codeBlockStartExp.capturedTexts()[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != HighlightBlockState::Normal) {
|
||||||
|
m_codeBlocksState.insert(blockNumber, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
block = block.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
src/peghighlighterresult.h
Normal file
75
src/peghighlighterresult.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#ifndef PEGHIGHLIGHTERRESULT_H
|
||||||
|
#define PEGHIGHLIGHTERRESULT_H
|
||||||
|
|
||||||
|
#include "vconstants.h"
|
||||||
|
#include "pegparser.h"
|
||||||
|
|
||||||
|
class PegMarkdownHighlighter;
|
||||||
|
class QTextDocument;
|
||||||
|
|
||||||
|
class PegHighlighterResult
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PegHighlighterResult();
|
||||||
|
|
||||||
|
PegHighlighterResult(const PegMarkdownHighlighter *p_peg,
|
||||||
|
const QSharedPointer<PegParseResult> &p_result);
|
||||||
|
|
||||||
|
bool matched(TimeStamp p_timeStamp) const;
|
||||||
|
|
||||||
|
TimeStamp m_timeStamp;
|
||||||
|
|
||||||
|
int m_numOfBlocks;
|
||||||
|
|
||||||
|
QVector<QVector<HLUnit>> m_blocksHighlights;
|
||||||
|
|
||||||
|
// Use another member to store the codeblocks highlights, because the highlight
|
||||||
|
// sequence is blockHighlights, regular-expression-based highlihgts, and then
|
||||||
|
// codeBlockHighlights.
|
||||||
|
// Support fenced code block only.
|
||||||
|
QVector<QVector<HLUnitStyle> > m_codeBlocksHighlights;
|
||||||
|
|
||||||
|
// All image link regions.
|
||||||
|
QVector<VElementRegion> m_imageRegions;
|
||||||
|
|
||||||
|
// All header regions.
|
||||||
|
// Sorted by start position.
|
||||||
|
QVector<VElementRegion> m_headerRegions;
|
||||||
|
|
||||||
|
// All fenced code blocks.
|
||||||
|
QVector<VCodeBlock> m_codeBlocks;
|
||||||
|
|
||||||
|
// Indexed by block number.
|
||||||
|
QHash<int, HighlightBlockState> m_codeBlocksState;
|
||||||
|
|
||||||
|
int m_numOfCodeBlockHighlightsToRecv;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Parse highlight elements for all the blocks from parse results.
|
||||||
|
void parseBlocksHighlights(const PegMarkdownHighlighter *p_peg,
|
||||||
|
const QSharedPointer<PegParseResult> &p_result);
|
||||||
|
|
||||||
|
// Parse highlight elements for blocks from one parse result.
|
||||||
|
void parseBlocksHighlightOne(const QTextDocument *p_doc,
|
||||||
|
unsigned long p_pos,
|
||||||
|
unsigned long p_end,
|
||||||
|
int p_styleIndex);
|
||||||
|
|
||||||
|
// Parse fenced code blocks from parse results.
|
||||||
|
void parseFencedCodeBlocks(const PegMarkdownHighlighter *p_peg,
|
||||||
|
const QSharedPointer<PegParseResult> &p_result);
|
||||||
|
|
||||||
|
void parseBlocksElementRegionOne(QHash<int, QVector<VElementRegion>> &p_regs,
|
||||||
|
const QTextDocument *p_doc,
|
||||||
|
unsigned long p_pos,
|
||||||
|
unsigned long p_end);
|
||||||
|
|
||||||
|
QRegExp m_codeBlockStartExp;
|
||||||
|
QRegExp m_codeBlockEndExp;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool PegHighlighterResult::matched(TimeStamp p_timeStamp) const
|
||||||
|
{
|
||||||
|
return m_timeStamp == p_timeStamp;
|
||||||
|
}
|
||||||
|
#endif // PEGHIGHLIGHTERRESULT_H
|
409
src/pegmarkdownhighlighter.cpp
Normal file
409
src/pegmarkdownhighlighter.cpp
Normal file
@ -0,0 +1,409 @@
|
|||||||
|
#include "pegmarkdownhighlighter.h"
|
||||||
|
|
||||||
|
#include <QTextDocument>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "pegparser.h"
|
||||||
|
#include "vconfigmanager.h"
|
||||||
|
#include "utils/vutils.h"
|
||||||
|
|
||||||
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
|
PegMarkdownHighlighter::PegMarkdownHighlighter(QTextDocument *p_doc)
|
||||||
|
: QSyntaxHighlighter(p_doc),
|
||||||
|
m_doc(p_doc),
|
||||||
|
m_timeStamp(0),
|
||||||
|
m_parser(NULL),
|
||||||
|
m_parserExts(pmh_EXT_STRIKE | pmh_EXT_FRONTMATTER)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::init(const QVector<HighlightingStyle> &p_styles,
|
||||||
|
const QHash<QString, QTextCharFormat> &p_codeBlockStyles,
|
||||||
|
bool p_mathjaxEnabled,
|
||||||
|
int p_timerInterval)
|
||||||
|
{
|
||||||
|
m_styles = p_styles;
|
||||||
|
m_codeBlockStyles = p_codeBlockStyles;
|
||||||
|
|
||||||
|
m_codeBlockFormat.setForeground(QBrush(Qt::darkYellow));
|
||||||
|
for (int index = 0; index < m_styles.size(); ++index) {
|
||||||
|
switch (m_styles[index].type) {
|
||||||
|
case pmh_FENCEDCODEBLOCK:
|
||||||
|
m_codeBlockFormat = m_styles[index].format;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_colorColumnFormat = m_codeBlockFormat;
|
||||||
|
m_colorColumnFormat.setForeground(QColor(g_config->getEditorColorColumnFg()));
|
||||||
|
m_colorColumnFormat.setBackground(QColor(g_config->getEditorColorColumnBg()));
|
||||||
|
|
||||||
|
m_result.reset(new PegHighlighterResult());
|
||||||
|
|
||||||
|
m_parser = new PegParser(this);
|
||||||
|
connect(m_parser, &PegParser::parseResultReady,
|
||||||
|
this, &PegMarkdownHighlighter::handleParseResult);
|
||||||
|
|
||||||
|
m_timer = new QTimer(this);
|
||||||
|
m_timer->setSingleShot(true);
|
||||||
|
m_timer->setInterval(p_timerInterval);
|
||||||
|
connect(m_timer, &QTimer::timeout,
|
||||||
|
this, [this]() {
|
||||||
|
startParse();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_doc, &QTextDocument::contentsChange,
|
||||||
|
this, &PegMarkdownHighlighter::handleContentsChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::highlightBlock(const QString &p_text)
|
||||||
|
{
|
||||||
|
QSharedPointer<PegHighlighterResult> result(m_result);
|
||||||
|
|
||||||
|
int blockNum = currentBlock().blockNumber();
|
||||||
|
if (result->m_blocksHighlights.size() > blockNum) {
|
||||||
|
// units are sorted by start position and length.
|
||||||
|
const QVector<HLUnit> &units = result->m_blocksHighlights[blockNum];
|
||||||
|
if (!units.isEmpty()) {
|
||||||
|
for (int i = 0; i < units.size(); ++i) {
|
||||||
|
const HLUnit &unit = units[i];
|
||||||
|
if (i == 0) {
|
||||||
|
// No need to merge format.
|
||||||
|
setFormat(unit.start,
|
||||||
|
unit.length,
|
||||||
|
m_styles[unit.styleIndex].format);
|
||||||
|
} else {
|
||||||
|
QTextCharFormat newFormat = m_styles[unit.styleIndex].format;
|
||||||
|
for (int j = i - 1; j >= 0; --j) {
|
||||||
|
if (units[j].start + units[j].length <= unit.start) {
|
||||||
|
// It won't affect current unit.
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// Merge the format.
|
||||||
|
QTextCharFormat tmpFormat(newFormat);
|
||||||
|
newFormat = m_styles[units[j].styleIndex].format;
|
||||||
|
// tmpFormat takes precedence.
|
||||||
|
newFormat.merge(tmpFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFormat(unit.start, unit.length, newFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set current block's user data.
|
||||||
|
updateBlockUserData(blockNum, p_text);
|
||||||
|
|
||||||
|
setCurrentBlockState(HighlightBlockState::Normal);
|
||||||
|
|
||||||
|
updateCodeBlockState(result, blockNum, p_text);
|
||||||
|
|
||||||
|
if (currentBlockState() == HighlightBlockState::CodeBlock) {
|
||||||
|
highlightCodeBlock(result, blockNum);
|
||||||
|
|
||||||
|
highlightCodeBlockColorColumn(p_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::handleContentsChange(int p_position, int p_charsRemoved, int p_charsAdded)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_position);
|
||||||
|
|
||||||
|
if (p_charsRemoved == 0 && p_charsAdded == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
++m_timeStamp;
|
||||||
|
|
||||||
|
// We still need a timer to start a complete parse.
|
||||||
|
m_timer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::startParse()
|
||||||
|
{
|
||||||
|
QSharedPointer<PegParseConfig> config(new PegParseConfig());
|
||||||
|
config->m_timeStamp = m_timeStamp;
|
||||||
|
config->m_data = m_doc->toPlainText().toUtf8();
|
||||||
|
config->m_numOfBlocks = m_doc->blockCount();
|
||||||
|
config->m_extensions = m_parserExts;
|
||||||
|
|
||||||
|
m_parser->parseAsync(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool compHLUnitStyle(const HLUnitStyle &a, const HLUnitStyle &b)
|
||||||
|
{
|
||||||
|
if (a.start < b.start) {
|
||||||
|
return true;
|
||||||
|
} else if (a.start == b.start) {
|
||||||
|
return a.length > b.length;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::setCodeBlockHighlights(TimeStamp p_timeStamp,
|
||||||
|
const QVector<HLUnitPos> &p_units)
|
||||||
|
{
|
||||||
|
QSharedPointer<PegHighlighterResult> result(m_result);
|
||||||
|
|
||||||
|
if (!result->matched(p_timeStamp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_units.isEmpty()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QVector<QVector<HLUnitStyle>> highlights(result->m_codeBlocksHighlights.size());
|
||||||
|
for (auto const &unit : p_units) {
|
||||||
|
int pos = unit.m_position;
|
||||||
|
int end = unit.m_position + unit.m_length;
|
||||||
|
QTextBlock block = m_doc->findBlock(pos);
|
||||||
|
int startBlockNum = block.blockNumber();
|
||||||
|
int endBlockNum = m_doc->findBlock(end).blockNumber();
|
||||||
|
|
||||||
|
// Text has been changed. Abandon the obsolete parsed result.
|
||||||
|
if (startBlockNum == -1 || endBlockNum >= highlights.size()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (block.isValid()) {
|
||||||
|
int blockNumber = block.blockNumber();
|
||||||
|
if (blockNumber > endBlockNum) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockStartPos = block.position();
|
||||||
|
HLUnitStyle hl;
|
||||||
|
hl.style = unit.m_style;
|
||||||
|
if (blockNumber == startBlockNum) {
|
||||||
|
hl.start = pos - blockStartPos;
|
||||||
|
hl.length = (startBlockNum == endBlockNum) ?
|
||||||
|
(end - pos) : (block.length() - hl.start);
|
||||||
|
} else if (blockNumber == endBlockNum) {
|
||||||
|
hl.start = 0;
|
||||||
|
hl.length = end - blockStartPos;
|
||||||
|
} else {
|
||||||
|
hl.start = 0;
|
||||||
|
hl.length = block.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
highlights[blockNumber].append(hl);
|
||||||
|
|
||||||
|
block = block.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to highlight in order.
|
||||||
|
for (int i = 0; i < highlights.size(); ++i) {
|
||||||
|
QVector<HLUnitStyle> &units = highlights[i];
|
||||||
|
if (!units.isEmpty()) {
|
||||||
|
if (units.size() > 1) {
|
||||||
|
std::sort(units.begin(), units.end(), compHLUnitStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
result->m_codeBlocksHighlights[i].append(units);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (--result->m_numOfCodeBlockHighlightsToRecv <= 0) {
|
||||||
|
rehighlight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::updateHighlightFast()
|
||||||
|
{
|
||||||
|
updateHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::updateHighlight()
|
||||||
|
{
|
||||||
|
m_timer->stop();
|
||||||
|
startParse();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::handleParseResult(const QSharedPointer<PegParseResult> &p_result)
|
||||||
|
{
|
||||||
|
if (!m_result.isNull() && m_result->m_timeStamp > p_result->m_timeStamp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PegHighlighterResult *pegRes = new PegHighlighterResult(this, p_result);
|
||||||
|
m_result.reset(pegRes);
|
||||||
|
|
||||||
|
updateCodeBlocks(m_result);
|
||||||
|
|
||||||
|
// Now we got a new result, rehighlight.
|
||||||
|
rehighlight();
|
||||||
|
|
||||||
|
completeHighlight(m_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::updateCodeBlocks(QSharedPointer<PegHighlighterResult> p_result)
|
||||||
|
{
|
||||||
|
if (!p_result->matched(m_timeStamp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_config->getEnableCodeBlockHighlight()) {
|
||||||
|
p_result->m_codeBlocksHighlights.resize(p_result->m_numOfBlocks);
|
||||||
|
p_result->m_numOfCodeBlockHighlightsToRecv = p_result->m_codeBlocks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit codeBlocksUpdated(p_result->m_timeStamp, p_result->m_codeBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::updateBlockUserData(int p_blockNum, const QString &p_text)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_text);
|
||||||
|
VTextBlockData *blockData = currentBlockData();
|
||||||
|
if (!blockData) {
|
||||||
|
blockData = new VTextBlockData();
|
||||||
|
setCurrentBlockUserData(blockData);
|
||||||
|
} else {
|
||||||
|
blockData->setCodeBlockIndentation(-1);
|
||||||
|
blockData->clearMathjax();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockData->getPreviews().isEmpty()) {
|
||||||
|
m_possiblePreviewBlocks.remove(p_blockNum);
|
||||||
|
} else {
|
||||||
|
m_possiblePreviewBlocks.insert(p_blockNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::updateCodeBlockState(const QSharedPointer<PegHighlighterResult> &p_result,
|
||||||
|
int p_blockNum,
|
||||||
|
const QString &p_text)
|
||||||
|
{
|
||||||
|
if (!p_result->matched(m_timeStamp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = p_result->m_codeBlocksState.find(p_blockNum);
|
||||||
|
if (it != p_result->m_codeBlocksState.end()) {
|
||||||
|
VTextBlockData *blockData = currentBlockData();
|
||||||
|
Q_ASSERT(blockData);
|
||||||
|
|
||||||
|
HighlightBlockState state = it.value();
|
||||||
|
// Set code block indentation.
|
||||||
|
switch (state) {
|
||||||
|
case HighlightBlockState::CodeBlockStart:
|
||||||
|
{
|
||||||
|
int startLeadingSpaces = 0;
|
||||||
|
QRegExp reg(VUtils::c_fencedCodeBlockStartRegExp);
|
||||||
|
int idx = reg.indexIn(p_text);
|
||||||
|
if (idx >= 0) {
|
||||||
|
startLeadingSpaces = reg.capturedTexts()[1].size();
|
||||||
|
}
|
||||||
|
|
||||||
|
blockData->setCodeBlockIndentation(startLeadingSpaces);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HighlightBlockState::CodeBlock:
|
||||||
|
V_FALLTHROUGH;
|
||||||
|
case HighlightBlockState::CodeBlockEnd:
|
||||||
|
{
|
||||||
|
int startLeadingSpaces = 0;
|
||||||
|
VTextBlockData *preBlockData = previousBlockData();
|
||||||
|
if (preBlockData) {
|
||||||
|
startLeadingSpaces = preBlockData->getCodeBlockIndentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
blockData->setCodeBlockIndentation(startLeadingSpaces);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set code block state.
|
||||||
|
setCurrentBlockState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::highlightCodeBlock(const QSharedPointer<PegHighlighterResult> &p_result,
|
||||||
|
int p_blockNum)
|
||||||
|
{
|
||||||
|
if (p_result->m_codeBlocksHighlights.size() > p_blockNum) {
|
||||||
|
const QVector<HLUnitStyle> &units = p_result->m_codeBlocksHighlights[p_blockNum];
|
||||||
|
if (!units.isEmpty()) {
|
||||||
|
QVector<QTextCharFormat *> formats(units.size(), NULL);
|
||||||
|
for (int i = 0; i < units.size(); ++i) {
|
||||||
|
const HLUnitStyle &unit = units[i];
|
||||||
|
auto it = m_codeBlockStyles.find(unit.style);
|
||||||
|
if (it == m_codeBlockStyles.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
formats[i] = &(*it);
|
||||||
|
|
||||||
|
QTextCharFormat newFormat = m_codeBlockFormat;
|
||||||
|
newFormat.merge(*it);
|
||||||
|
for (int j = i - 1; j >= 0; --j) {
|
||||||
|
if (units[j].start + units[j].length <= unit.start) {
|
||||||
|
// It won't affect current unit.
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// Merge the format.
|
||||||
|
if (formats[j]) {
|
||||||
|
QTextCharFormat tmpFormat(newFormat);
|
||||||
|
newFormat = *(formats[j]);
|
||||||
|
// tmpFormat takes precedence.
|
||||||
|
newFormat.merge(tmpFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFormat(unit.start, unit.length, newFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::highlightCodeBlockColorColumn(const QString &p_text)
|
||||||
|
{
|
||||||
|
int cc = g_config->getColorColumn();
|
||||||
|
if (cc <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VTextBlockData *blockData = currentBlockData();
|
||||||
|
Q_ASSERT(blockData);
|
||||||
|
int indent = blockData->getCodeBlockIndentation();
|
||||||
|
if (indent == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cc += indent;
|
||||||
|
if (p_text.size() < cc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFormat(cc - 1, 1, m_colorColumnFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::completeHighlight(QSharedPointer<PegHighlighterResult> p_result)
|
||||||
|
{
|
||||||
|
if (!p_result->matched(m_timeStamp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit imageLinksUpdated(p_result->m_imageRegions);
|
||||||
|
emit headersUpdated(p_result->m_headerRegions);
|
||||||
|
|
||||||
|
emit highlightCompleted();
|
||||||
|
}
|
179
src/pegmarkdownhighlighter.h
Normal file
179
src/pegmarkdownhighlighter.h
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
#ifndef PEGMARKDOWNHIGHLIGHTER_H
|
||||||
|
#define PEGMARKDOWNHIGHLIGHTER_H
|
||||||
|
|
||||||
|
#include <QSyntaxHighlighter>
|
||||||
|
#include <QTextCharFormat>
|
||||||
|
|
||||||
|
#include "vtextblockdata.h"
|
||||||
|
#include "markdownhighlighterdata.h"
|
||||||
|
#include "peghighlighterresult.h"
|
||||||
|
|
||||||
|
class PegParser;
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
|
class PegMarkdownHighlighter : public QSyntaxHighlighter
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PegMarkdownHighlighter(QTextDocument *p_doc = nullptr);
|
||||||
|
|
||||||
|
void init(const QVector<HighlightingStyle> &p_styles,
|
||||||
|
const QHash<QString, QTextCharFormat> &p_codeBlockStyles,
|
||||||
|
bool p_mathjaxEnabled,
|
||||||
|
int p_timerInterval);
|
||||||
|
|
||||||
|
// Set code block highlight result by VCodeBlockHighlightHelper.
|
||||||
|
void setCodeBlockHighlights(TimeStamp p_timeStamp, const QVector<HLUnitPos> &p_units);
|
||||||
|
|
||||||
|
const QVector<VElementRegion> &getHeaderRegions() const;
|
||||||
|
|
||||||
|
const QSet<int> &getPossiblePreviewBlocks() const;
|
||||||
|
|
||||||
|
void clearPossiblePreviewBlocks(const QVector<int> &p_blocksToClear);
|
||||||
|
|
||||||
|
void addPossiblePreviewBlock(int p_blockNumber);
|
||||||
|
|
||||||
|
// Parse and only update the highlight results for rehighlight().
|
||||||
|
void updateHighlightFast();
|
||||||
|
|
||||||
|
QHash<QString, QTextCharFormat> &getCodeBlockStyles();
|
||||||
|
|
||||||
|
QVector<HighlightingStyle> &getStyles();
|
||||||
|
|
||||||
|
const QVector<HighlightingStyle> &getStyles() const;
|
||||||
|
|
||||||
|
const QTextDocument *getDocument() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// Parse and rehighlight immediately.
|
||||||
|
void updateHighlight();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void highlightCompleted();
|
||||||
|
|
||||||
|
// QVector is implicitly shared.
|
||||||
|
void codeBlocksUpdated(TimeStamp p_timeStamp, const QVector<VCodeBlock> &p_codeBlocks);
|
||||||
|
|
||||||
|
// Emitted when image regions have been fetched from a new parsing result.
|
||||||
|
void imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions);
|
||||||
|
|
||||||
|
// Emitted when header regions have been fetched from a new parsing result.
|
||||||
|
void headersUpdated(const QVector<VElementRegion> &p_headerRegions);
|
||||||
|
|
||||||
|
// Emitted when Mathjax blocks updated.
|
||||||
|
void mathjaxBlocksUpdated(const QVector<VMathjaxBlock> &p_mathjaxBlocks);
|
||||||
|
|
||||||
|
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<PegParseResult> &p_result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void startParse();
|
||||||
|
|
||||||
|
void updateCodeBlocks(QSharedPointer<PegHighlighterResult> p_result);
|
||||||
|
|
||||||
|
// Set the user data of currentBlock().
|
||||||
|
void updateBlockUserData(int p_blockNum, const QString &p_text);
|
||||||
|
|
||||||
|
void updateCodeBlockState(const QSharedPointer<PegHighlighterResult> &p_result,
|
||||||
|
int p_blockNum,
|
||||||
|
const QString &p_text);
|
||||||
|
|
||||||
|
// Highlight fenced code block according to VCodeBlockHighlightHelper result.
|
||||||
|
void highlightCodeBlock(const QSharedPointer<PegHighlighterResult> &p_result,
|
||||||
|
int p_blockNum);
|
||||||
|
|
||||||
|
// Highlight color column in code block.
|
||||||
|
void highlightCodeBlockColorColumn(const QString &p_text);
|
||||||
|
|
||||||
|
VTextBlockData *currentBlockData() const;
|
||||||
|
|
||||||
|
VTextBlockData *previousBlockData() const;
|
||||||
|
|
||||||
|
void completeHighlight(QSharedPointer<PegHighlighterResult> p_result);
|
||||||
|
|
||||||
|
QTextDocument *m_doc;
|
||||||
|
|
||||||
|
TimeStamp m_timeStamp;
|
||||||
|
|
||||||
|
QVector<HighlightingStyle> m_styles;
|
||||||
|
QHash<QString, QTextCharFormat> m_codeBlockStyles;
|
||||||
|
|
||||||
|
QTextCharFormat m_codeBlockFormat;
|
||||||
|
QTextCharFormat m_colorColumnFormat;
|
||||||
|
|
||||||
|
PegParser *m_parser;
|
||||||
|
|
||||||
|
QSharedPointer<PegHighlighterResult> m_result;
|
||||||
|
|
||||||
|
// Block number of those blocks which possible contains previewed image.
|
||||||
|
QSet<int> m_possiblePreviewBlocks;
|
||||||
|
|
||||||
|
// Extensions for parser.
|
||||||
|
int m_parserExts;
|
||||||
|
|
||||||
|
// Timer to trigger parse.
|
||||||
|
QTimer *m_timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const QVector<VElementRegion> &PegMarkdownHighlighter::getHeaderRegions() const
|
||||||
|
{
|
||||||
|
return m_result->m_headerRegions;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QSet<int> &PegMarkdownHighlighter::getPossiblePreviewBlocks() const
|
||||||
|
{
|
||||||
|
return m_possiblePreviewBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void PegMarkdownHighlighter::clearPossiblePreviewBlocks(const QVector<int> &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<QString, QTextCharFormat> &PegMarkdownHighlighter::getCodeBlockStyles()
|
||||||
|
{
|
||||||
|
return m_codeBlockStyles;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QVector<HighlightingStyle> &PegMarkdownHighlighter::getStyles()
|
||||||
|
{
|
||||||
|
return m_styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QVector<HighlightingStyle> &PegMarkdownHighlighter::getStyles() const
|
||||||
|
{
|
||||||
|
return m_styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QTextDocument *PegMarkdownHighlighter::getDocument() const
|
||||||
|
{
|
||||||
|
return m_doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VTextBlockData *PegMarkdownHighlighter::currentBlockData() const
|
||||||
|
{
|
||||||
|
return static_cast<VTextBlockData *>(currentBlockUserData());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VTextBlockData *PegMarkdownHighlighter::previousBlockData() const
|
||||||
|
{
|
||||||
|
QTextBlock block = currentBlock().previous();
|
||||||
|
if (!block.isValid()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<VTextBlockData *>(block.userData());
|
||||||
|
}
|
||||||
|
#endif // PEGMARKDOWNHIGHLIGHTER_H
|
276
src/pegparser.cpp
Normal file
276
src/pegparser.cpp
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
#include "pegparser.h"
|
||||||
|
|
||||||
|
enum WorkerState
|
||||||
|
{
|
||||||
|
Idle,
|
||||||
|
Busy,
|
||||||
|
Cancelled,
|
||||||
|
Finished
|
||||||
|
};
|
||||||
|
|
||||||
|
void PegParseResult::parse(QAtomicInt &p_stop)
|
||||||
|
{
|
||||||
|
parseImageRegions(p_stop);
|
||||||
|
|
||||||
|
parseHeaderRegions(p_stop);
|
||||||
|
|
||||||
|
parseFencedCodeBlockRegions(p_stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParseResult::parseImageRegions(QAtomicInt &p_stop)
|
||||||
|
{
|
||||||
|
// From Qt5.7, the capacity is preserved.
|
||||||
|
m_imageRegions.clear();
|
||||||
|
if (isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmh_element *elem = m_pmhElements[pmh_IMAGE];
|
||||||
|
while (elem != NULL) {
|
||||||
|
if (elem->end <= elem->pos) {
|
||||||
|
elem = elem->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_stop.load() == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_imageRegions.push_back(VElementRegion(elem->pos, elem->end));
|
||||||
|
elem = elem->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParseResult::parseHeaderRegions(QAtomicInt &p_stop)
|
||||||
|
{
|
||||||
|
// From Qt5.7, the capacity is preserved.
|
||||||
|
m_headerRegions.clear();
|
||||||
|
if (isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmh_element_type hx[6] = {pmh_H1, pmh_H2, pmh_H3, pmh_H4, pmh_H5, pmh_H6};
|
||||||
|
for (int i = 0; i < 6; ++i) {
|
||||||
|
pmh_element *elem = m_pmhElements[hx[i]];
|
||||||
|
while (elem != NULL) {
|
||||||
|
if (elem->end <= elem->pos) {
|
||||||
|
elem = elem->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_stop.load() == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_headerRegions.push_back(VElementRegion(elem->pos, elem->end));
|
||||||
|
elem = elem->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_stop.load() == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(m_headerRegions.begin(), m_headerRegions.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParseResult::parseFencedCodeBlockRegions(QAtomicInt &p_stop)
|
||||||
|
{
|
||||||
|
m_codeBlockRegions.clear();
|
||||||
|
if (isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmh_element *elem = m_pmhElements[pmh_FENCEDCODEBLOCK];
|
||||||
|
while (elem != NULL) {
|
||||||
|
if (elem->end <= elem->pos) {
|
||||||
|
elem = elem->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_stop.load() == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_codeBlockRegions.contains(elem->pos)) {
|
||||||
|
m_codeBlockRegions.insert(elem->pos, VElementRegion(elem->pos, elem->end));
|
||||||
|
}
|
||||||
|
|
||||||
|
elem = elem->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PegParserWorker::PegParserWorker(QObject *p_parent)
|
||||||
|
: QThread(p_parent),
|
||||||
|
m_stop(0),
|
||||||
|
m_state(WorkerState::Idle)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParserWorker::prepareParse(const QSharedPointer<PegParseConfig> &p_config)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_parseConfig.isNull());
|
||||||
|
|
||||||
|
m_state = WorkerState::Busy;
|
||||||
|
m_parseConfig = p_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParserWorker::reset()
|
||||||
|
{
|
||||||
|
m_parseConfig.reset();
|
||||||
|
m_parseResult.reset();
|
||||||
|
m_stop.store(0);
|
||||||
|
m_state = WorkerState::Idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParserWorker::stop()
|
||||||
|
{
|
||||||
|
m_stop.store(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParserWorker::run()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_state == WorkerState::Busy);
|
||||||
|
|
||||||
|
m_parseResult = parseMarkdown(m_parseConfig, m_stop);
|
||||||
|
|
||||||
|
if (isAskedToStop()) {
|
||||||
|
m_state = WorkerState::Cancelled;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = WorkerState::Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<PegParseResult> PegParserWorker::parseMarkdown(const QSharedPointer<PegParseConfig> &p_config,
|
||||||
|
QAtomicInt &p_stop)
|
||||||
|
{
|
||||||
|
QSharedPointer<PegParseResult> result(new PegParseResult(p_config));
|
||||||
|
|
||||||
|
if (p_config->m_data.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmh_element **pmhResult = NULL;
|
||||||
|
|
||||||
|
char *data = p_config->m_data.data();
|
||||||
|
|
||||||
|
pmh_markdown_to_elements(data, p_config->m_extensions, &pmhResult);
|
||||||
|
|
||||||
|
result->m_pmhElements = pmhResult;
|
||||||
|
|
||||||
|
if (p_stop.load() == 1) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->parse(p_stop);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define NUM_OF_THREADS 2
|
||||||
|
|
||||||
|
PegParser::PegParser(QObject *p_parent)
|
||||||
|
: QObject(p_parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParser::parseAsync(const QSharedPointer<PegParseConfig> &p_config)
|
||||||
|
{
|
||||||
|
m_pendingWork = p_config;
|
||||||
|
|
||||||
|
pickWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParser::init()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NUM_OF_THREADS; ++i) {
|
||||||
|
PegParserWorker *th = new PegParserWorker(this);
|
||||||
|
connect(th, &PegParserWorker::finished,
|
||||||
|
this, [this, th]() {
|
||||||
|
handleWorkerFinished(th);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_workers.append(th);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParser::clear()
|
||||||
|
{
|
||||||
|
m_pendingWork.reset();
|
||||||
|
|
||||||
|
for (auto const & th : m_workers) {
|
||||||
|
th->quit();
|
||||||
|
th->wait();
|
||||||
|
|
||||||
|
delete th;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_workers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
PegParser::~PegParser()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParser::handleWorkerFinished(PegParserWorker *p_worker)
|
||||||
|
{
|
||||||
|
QSharedPointer<PegParseResult> result;
|
||||||
|
if (p_worker->state() == WorkerState::Finished) {
|
||||||
|
result = p_worker->parseResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
p_worker->reset();
|
||||||
|
|
||||||
|
pickWorker();
|
||||||
|
|
||||||
|
if (!result.isNull()) {
|
||||||
|
emit parseResultReady(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParser::pickWorker()
|
||||||
|
{
|
||||||
|
if (m_pendingWork.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allBusy = true;
|
||||||
|
for (auto th : m_workers) {
|
||||||
|
if (th->state() == WorkerState::Idle) {
|
||||||
|
scheduleWork(th, m_pendingWork);
|
||||||
|
m_pendingWork.reset();
|
||||||
|
return;
|
||||||
|
} else if (th->state() != WorkerState::Busy) {
|
||||||
|
allBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allBusy) {
|
||||||
|
// Need to stop the worker with non-minimal timestamp.
|
||||||
|
int idx = 0;
|
||||||
|
TimeStamp minTS = m_workers[idx]->workTimeStamp();
|
||||||
|
|
||||||
|
if (m_workers.size() > 1) {
|
||||||
|
if (m_workers[1]->workTimeStamp() > minTS) {
|
||||||
|
idx = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_workers[idx]->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PegParser::scheduleWork(PegParserWorker *p_worker,
|
||||||
|
const QSharedPointer<PegParseConfig> &p_config)
|
||||||
|
{
|
||||||
|
Q_ASSERT(p_worker->state() == WorkerState::Idle);
|
||||||
|
|
||||||
|
p_worker->reset();
|
||||||
|
p_worker->prepareParse(p_config);
|
||||||
|
p_worker->start();
|
||||||
|
}
|
186
src/pegparser.h
Normal file
186
src/pegparser.h
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#ifndef PEGPARSER_H
|
||||||
|
#define PEGPARSER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QAtomicInt>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "vconstants.h"
|
||||||
|
#include "markdownhighlighterdata.h"
|
||||||
|
|
||||||
|
struct PegParseConfig
|
||||||
|
{
|
||||||
|
TimeStamp m_timeStamp;
|
||||||
|
|
||||||
|
QByteArray m_data;
|
||||||
|
|
||||||
|
int m_numOfBlocks;
|
||||||
|
|
||||||
|
int m_extensions;
|
||||||
|
|
||||||
|
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<PegParseConfig> &p_config)
|
||||||
|
: m_timeStamp(p_config->m_timeStamp),
|
||||||
|
m_numOfBlocks(p_config->m_numOfBlocks),
|
||||||
|
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);
|
||||||
|
|
||||||
|
TimeStamp m_timeStamp;
|
||||||
|
|
||||||
|
int m_numOfBlocks;
|
||||||
|
|
||||||
|
pmh_element **m_pmhElements;
|
||||||
|
|
||||||
|
// All image link regions.
|
||||||
|
QVector<VElementRegion> m_imageRegions;
|
||||||
|
|
||||||
|
// All header regions.
|
||||||
|
// Sorted by start position.
|
||||||
|
QVector<VElementRegion> m_headerRegions;
|
||||||
|
|
||||||
|
// Fenced code block regions.
|
||||||
|
// Ordered by start position in ascending order.
|
||||||
|
QMap<int, VElementRegion> m_codeBlockRegions;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void parseImageRegions(QAtomicInt &p_stop);
|
||||||
|
|
||||||
|
void parseHeaderRegions(QAtomicInt &p_stop);
|
||||||
|
|
||||||
|
void parseFencedCodeBlockRegions(QAtomicInt &p_stop);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PegParserWorker : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PegParserWorker(QObject *p_parent = nullptr);
|
||||||
|
|
||||||
|
void prepareParse(const QSharedPointer<PegParseConfig> &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<PegParseConfig> &parseConfig() const
|
||||||
|
{
|
||||||
|
return m_parseConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QSharedPointer<PegParseResult> &parseResult() const
|
||||||
|
{
|
||||||
|
return m_parseResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<PegParseResult> parseMarkdown(const QSharedPointer<PegParseConfig> &p_config,
|
||||||
|
QAtomicInt &p_stop);
|
||||||
|
|
||||||
|
bool isAskedToStop() const
|
||||||
|
{
|
||||||
|
return m_stop.load() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAtomicInt m_stop;
|
||||||
|
|
||||||
|
int m_state;
|
||||||
|
|
||||||
|
QSharedPointer<PegParseConfig> m_parseConfig;
|
||||||
|
|
||||||
|
QSharedPointer<PegParseResult> m_parseResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PegParser : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PegParser(QObject *p_parent = nullptr);
|
||||||
|
|
||||||
|
~PegParser();
|
||||||
|
|
||||||
|
void parseAsync(const QSharedPointer<PegParseConfig> &p_config);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void parseResultReady(const QSharedPointer<PegParseResult> &p_result);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleWorkerFinished(PegParserWorker *p_worker);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
void pickWorker();
|
||||||
|
|
||||||
|
void scheduleWork(PegParserWorker *p_worker, const QSharedPointer<PegParseConfig> &p_config);
|
||||||
|
|
||||||
|
// Maintain a fixed number of workers to pick work.
|
||||||
|
QVector<PegParserWorker *> m_workers;
|
||||||
|
|
||||||
|
QSharedPointer<PegParseConfig> m_pendingWork;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PEGPARSER_H
|
@ -106,7 +106,7 @@ minimize_to_system_tray=-1
|
|||||||
markdown_suffix=md,markdown,mkd
|
markdown_suffix=md,markdown,mkd
|
||||||
|
|
||||||
; Markdown highlight timer interval (milliseconds)
|
; Markdown highlight timer interval (milliseconds)
|
||||||
markdown_highlight_interval=400
|
markdown_highlight_interval=200
|
||||||
|
|
||||||
; Adds specified height between lines (in pixels)
|
; Adds specified height between lines (in pixels)
|
||||||
line_distance_height=3
|
line_distance_height=3
|
||||||
|
11
src/src.pro
11
src/src.pro
@ -140,7 +140,10 @@ SOURCES += main.cpp\
|
|||||||
vtagpanel.cpp \
|
vtagpanel.cpp \
|
||||||
valltagspanel.cpp \
|
valltagspanel.cpp \
|
||||||
vtaglabel.cpp \
|
vtaglabel.cpp \
|
||||||
vtagexplorer.cpp
|
vtagexplorer.cpp \
|
||||||
|
pegmarkdownhighlighter.cpp \
|
||||||
|
pegparser.cpp \
|
||||||
|
peghighlighterresult.cpp
|
||||||
|
|
||||||
HEADERS += vmainwindow.h \
|
HEADERS += vmainwindow.h \
|
||||||
vdirectorytree.h \
|
vdirectorytree.h \
|
||||||
@ -274,7 +277,11 @@ HEADERS += vmainwindow.h \
|
|||||||
vtagpanel.h \
|
vtagpanel.h \
|
||||||
valltagspanel.h \
|
valltagspanel.h \
|
||||||
vtaglabel.h \
|
vtaglabel.h \
|
||||||
vtagexplorer.h
|
vtagexplorer.h \
|
||||||
|
markdownhighlighterdata.h \
|
||||||
|
pegmarkdownhighlighter.h \
|
||||||
|
pegparser.h \
|
||||||
|
peghighlighterresult.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
vnote.qrc \
|
vnote.qrc \
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
#include "vdocument.h"
|
#include "vdocument.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
|
#include "pegmarkdownhighlighter.h"
|
||||||
|
|
||||||
VCodeBlockHighlightHelper::VCodeBlockHighlightHelper(HGMarkdownHighlighter *p_highlighter,
|
VCodeBlockHighlightHelper::VCodeBlockHighlightHelper(PegMarkdownHighlighter *p_highlighter,
|
||||||
VDocument *p_vdoc,
|
VDocument *p_vdoc,
|
||||||
MarkdownConverterType p_type)
|
MarkdownConverterType p_type)
|
||||||
: QObject(p_highlighter),
|
: QObject(p_highlighter),
|
||||||
@ -14,14 +16,14 @@ VCodeBlockHighlightHelper::VCodeBlockHighlightHelper(HGMarkdownHighlighter *p_hi
|
|||||||
m_type(p_type),
|
m_type(p_type),
|
||||||
m_timeStamp(0)
|
m_timeStamp(0)
|
||||||
{
|
{
|
||||||
connect(m_highlighter, &HGMarkdownHighlighter::codeBlocksUpdated,
|
connect(m_highlighter, &PegMarkdownHighlighter::codeBlocksUpdated,
|
||||||
this, &VCodeBlockHighlightHelper::handleCodeBlocksUpdated);
|
this, &VCodeBlockHighlightHelper::handleCodeBlocksUpdated);
|
||||||
connect(m_vdocument, &VDocument::textHighlighted,
|
connect(m_vdocument, &VDocument::textHighlighted,
|
||||||
this, &VCodeBlockHighlightHelper::handleTextHighlightResult);
|
this, &VCodeBlockHighlightHelper::handleTextHighlightResult);
|
||||||
|
|
||||||
// Web side is ready for code block highlight.
|
// Web side is ready for code block highlight.
|
||||||
connect(m_vdocument, &VDocument::readyToHighlightText,
|
connect(m_vdocument, &VDocument::readyToHighlightText,
|
||||||
m_highlighter, &HGMarkdownHighlighter::updateHighlight);
|
m_highlighter, &PegMarkdownHighlighter::updateHighlight);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString VCodeBlockHighlightHelper::unindentCodeBlock(const QString &p_text)
|
QString VCodeBlockHighlightHelper::unindentCodeBlock(const QString &p_text)
|
||||||
@ -57,44 +59,45 @@ QString VCodeBlockHighlightHelper::unindentCodeBlock(const QString &p_text)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCodeBlockHighlightHelper::handleCodeBlocksUpdated(const QVector<VCodeBlock> &p_codeBlocks)
|
void VCodeBlockHighlightHelper::handleCodeBlocksUpdated(TimeStamp p_timeStamp,
|
||||||
|
const QVector<VCodeBlock> &p_codeBlocks)
|
||||||
{
|
{
|
||||||
if (!m_vdocument->isReadyToHighlight()) {
|
if (!m_vdocument->isReadyToHighlight()) {
|
||||||
// Immediately return empty results.
|
// Immediately return empty results.
|
||||||
QVector<HLUnitPos> emptyRes;
|
QVector<HLUnitPos> emptyRes;
|
||||||
for (int i = 0; i < p_codeBlocks.size(); ++i) {
|
for (int i = 0; i < p_codeBlocks.size(); ++i) {
|
||||||
updateHighlightResults(0, emptyRes);
|
updateHighlightResults(p_timeStamp, 0, emptyRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int curStamp = m_timeStamp.fetchAndAddRelaxed(1) + 1;
|
m_timeStamp = p_timeStamp;
|
||||||
m_codeBlocks = p_codeBlocks;
|
m_codeBlocks = p_codeBlocks;
|
||||||
for (int i = 0; i < m_codeBlocks.size(); ++i) {
|
for (int i = 0; i < m_codeBlocks.size(); ++i) {
|
||||||
const VCodeBlock &block = m_codeBlocks[i];
|
const VCodeBlock &block = m_codeBlocks[i];
|
||||||
auto it = m_cache.find(block.m_text);
|
auto it = m_cache.find(block.m_text);
|
||||||
if (it != m_cache.end()) {
|
if (it != m_cache.end()) {
|
||||||
// Hit cache.
|
// Hit cache.
|
||||||
qDebug() << "code block highlight hit cache" << curStamp << i;
|
qDebug() << "code block highlight hit cache" << p_timeStamp << i;
|
||||||
it.value().m_timeStamp = curStamp;
|
it.value().m_timeStamp = p_timeStamp;
|
||||||
updateHighlightResults(block.m_startPos, it.value().m_units);
|
updateHighlightResults(p_timeStamp, block.m_startPos, it.value().m_units);
|
||||||
} else {
|
} else {
|
||||||
QString unindentedText = unindentCodeBlock(block.m_text);
|
QString unindentedText = unindentCodeBlock(block.m_text);
|
||||||
m_vdocument->highlightTextAsync(unindentedText, i, curStamp);
|
m_vdocument->highlightTextAsync(unindentedText, i, p_timeStamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCodeBlockHighlightHelper::handleTextHighlightResult(const QString &p_html,
|
void VCodeBlockHighlightHelper::handleTextHighlightResult(const QString &p_html,
|
||||||
int p_id,
|
int p_id,
|
||||||
int p_timeStamp)
|
unsigned long long p_timeStamp)
|
||||||
{
|
{
|
||||||
int curStamp = m_timeStamp.load();
|
|
||||||
// Abandon obsolete result.
|
// Abandon obsolete result.
|
||||||
if (curStamp != p_timeStamp) {
|
if (m_timeStamp != p_timeStamp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseHighlightResult(p_timeStamp, p_id, p_html);
|
parseHighlightResult(p_timeStamp, p_id, p_html);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +140,7 @@ static void matchTokenRelaxed(const QString &p_text, const QString &p_tokenStr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For now, we could only handle code blocks outside the list.
|
// For now, we could only handle code blocks outside the list.
|
||||||
void VCodeBlockHighlightHelper::parseHighlightResult(int p_timeStamp,
|
void VCodeBlockHighlightHelper::parseHighlightResult(TimeStamp p_timeStamp,
|
||||||
int p_idx,
|
int p_idx,
|
||||||
const QString &p_html)
|
const QString &p_html)
|
||||||
{
|
{
|
||||||
@ -209,9 +212,8 @@ void VCodeBlockHighlightHelper::parseHighlightResult(int p_timeStamp,
|
|||||||
|
|
||||||
exit:
|
exit:
|
||||||
// Pass result back to highlighter.
|
// Pass result back to highlighter.
|
||||||
int curStamp = m_timeStamp.load();
|
|
||||||
// Abandon obsolete result.
|
// Abandon obsolete result.
|
||||||
if (curStamp != p_timeStamp) {
|
if (m_timeStamp != p_timeStamp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,10 +226,11 @@ exit:
|
|||||||
// Add it to cache.
|
// Add it to cache.
|
||||||
addToHighlightCache(text, p_timeStamp, hlUnits);
|
addToHighlightCache(text, p_timeStamp, hlUnits);
|
||||||
|
|
||||||
updateHighlightResults(startPos, hlUnits);
|
updateHighlightResults(p_timeStamp, startPos, hlUnits);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCodeBlockHighlightHelper::updateHighlightResults(int p_startPos,
|
void VCodeBlockHighlightHelper::updateHighlightResults(TimeStamp p_timeStamp,
|
||||||
|
int p_startPos,
|
||||||
QVector<HLUnitPos> p_units)
|
QVector<HLUnitPos> p_units)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < p_units.size(); ++i) {
|
for (int i = 0; i < p_units.size(); ++i) {
|
||||||
@ -235,7 +238,7 @@ void VCodeBlockHighlightHelper::updateHighlightResults(int p_startPos,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We need to call this function anyway to trigger the rehighlight.
|
// We need to call this function anyway to trigger the rehighlight.
|
||||||
m_highlighter->setCodeBlockHighlights(p_units);
|
m_highlighter->setCodeBlockHighlights(p_timeStamp, p_units);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VCodeBlockHighlightHelper::parseSpanElement(QXmlStreamReader &p_xml,
|
bool VCodeBlockHighlightHelper::parseSpanElement(QXmlStreamReader &p_xml,
|
||||||
@ -283,7 +286,7 @@ bool VCodeBlockHighlightHelper::parseSpanElement(QXmlStreamReader &p_xml,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VCodeBlockHighlightHelper::addToHighlightCache(const QString &p_text,
|
void VCodeBlockHighlightHelper::addToHighlightCache(const QString &p_text,
|
||||||
int p_timeStamp,
|
TimeStamp p_timeStamp,
|
||||||
const QVector<HLUnitPos> &p_units)
|
const QVector<HLUnitPos> &p_units)
|
||||||
{
|
{
|
||||||
const int c_maxEntries = 100;
|
const int c_maxEntries = 100;
|
||||||
|
@ -6,15 +6,17 @@
|
|||||||
#include <QAtomicInteger>
|
#include <QAtomicInteger>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
|
|
||||||
class VDocument;
|
class VDocument;
|
||||||
|
class PegMarkdownHighlighter;
|
||||||
|
|
||||||
class VCodeBlockHighlightHelper : public QObject
|
class VCodeBlockHighlightHelper : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
VCodeBlockHighlightHelper(HGMarkdownHighlighter *p_highlighter,
|
VCodeBlockHighlightHelper(PegMarkdownHighlighter *p_highlighter,
|
||||||
VDocument *p_vdoc, MarkdownConverterType p_type);
|
VDocument *p_vdoc, MarkdownConverterType p_type);
|
||||||
|
|
||||||
// @p_text: text of fenced code block.
|
// @p_text: text of fenced code block.
|
||||||
@ -25,27 +27,30 @@ public:
|
|||||||
static QString unindentCodeBlock(const QString &p_text);
|
static QString unindentCodeBlock(const QString &p_text);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleCodeBlocksUpdated(const QVector<VCodeBlock> &p_codeBlocks);
|
void handleCodeBlocksUpdated(TimeStamp p_timeStamp, const QVector<VCodeBlock> &p_codeBlocks);
|
||||||
|
|
||||||
void handleTextHighlightResult(const QString &p_html, int p_id, int p_timeStamp);
|
void handleTextHighlightResult(const QString &p_html, int p_id, unsigned long long p_timeStamp);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct HLResult
|
struct HLResult
|
||||||
{
|
{
|
||||||
HLResult() : m_timeStamp(-1)
|
HLResult()
|
||||||
|
: m_timeStamp(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
HLResult(int p_timeStamp, const QVector<HLUnitPos> &p_units)
|
HLResult(TimeStamp p_timeStamp, const QVector<HLUnitPos> &p_units)
|
||||||
: m_timeStamp(p_timeStamp), m_units(p_units)
|
: m_timeStamp(p_timeStamp),
|
||||||
|
m_units(p_units)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int m_timeStamp;
|
TimeStamp m_timeStamp;
|
||||||
|
|
||||||
QVector<HLUnitPos> m_units;
|
QVector<HLUnitPos> m_units;
|
||||||
};
|
};
|
||||||
|
|
||||||
void parseHighlightResult(int p_timeStamp, int p_idx, const QString &p_html);
|
void parseHighlightResult(TimeStamp p_timeStamp, int p_idx, const QString &p_html);
|
||||||
|
|
||||||
// @p_text: the raw text of the code block;
|
// @p_text: the raw text of the code block;
|
||||||
// @p_index: the start index of the span element within @p_text;
|
// @p_index: the start index of the span element within @p_text;
|
||||||
@ -54,16 +59,18 @@ private:
|
|||||||
const QString &p_text, int &p_index,
|
const QString &p_text, int &p_index,
|
||||||
QVector<HLUnitPos> &p_units);
|
QVector<HLUnitPos> &p_units);
|
||||||
|
|
||||||
void updateHighlightResults(int p_startPos, QVector<HLUnitPos> p_units);
|
void updateHighlightResults(TimeStamp p_timeStamp, int p_startPos, QVector<HLUnitPos> p_units);
|
||||||
|
|
||||||
void addToHighlightCache(const QString &p_text,
|
void addToHighlightCache(const QString &p_text,
|
||||||
int p_timeStamp,
|
TimeStamp p_timeStamp,
|
||||||
const QVector<HLUnitPos> &p_units);
|
const QVector<HLUnitPos> &p_units);
|
||||||
|
|
||||||
HGMarkdownHighlighter *m_highlighter;
|
PegMarkdownHighlighter *m_highlighter;
|
||||||
VDocument *m_vdocument;
|
VDocument *m_vdocument;
|
||||||
MarkdownConverterType m_type;
|
MarkdownConverterType m_type;
|
||||||
QAtomicInteger<int> m_timeStamp;
|
|
||||||
|
TimeStamp m_timeStamp;
|
||||||
|
|
||||||
QVector<VCodeBlock> m_codeBlocks;
|
QVector<VCodeBlock> m_codeBlocks;
|
||||||
|
|
||||||
// Cache for highlight result, using the code block text as key.
|
// Cache for highlight result, using the code block text as key.
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
#include "vnotebook.h"
|
#include "vnotebook.h"
|
||||||
#include "hgmarkdownhighlighter.h"
|
#include "markdownhighlighterdata.h"
|
||||||
#include "vmarkdownconverter.h"
|
#include "vmarkdownconverter.h"
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
#include "vfilesessioninfo.h"
|
#include "vfilesessioninfo.h"
|
||||||
|
@ -122,9 +122,11 @@ enum HighlightBlockState
|
|||||||
CodeBlockEnd,
|
CodeBlockEnd,
|
||||||
|
|
||||||
// This block is inside a HTML comment region.
|
// This block is inside a HTML comment region.
|
||||||
|
// Obsolete.
|
||||||
Comment,
|
Comment,
|
||||||
|
|
||||||
// Verbatim code block.
|
// Verbatim code block.
|
||||||
|
// Obsolete.
|
||||||
Verbatim,
|
Verbatim,
|
||||||
|
|
||||||
// Mathjax. It means the pending state of the block.
|
// Mathjax. It means the pending state of the block.
|
||||||
@ -132,6 +134,7 @@ enum HighlightBlockState
|
|||||||
MathjaxInline,
|
MathjaxInline,
|
||||||
|
|
||||||
// Header.
|
// Header.
|
||||||
|
// Obsolete.
|
||||||
Header
|
Header
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,12 +74,12 @@ void VDocument::keyPressEvent(int p_key, bool p_ctrl, bool p_shift, bool p_meta)
|
|||||||
emit keyPressed(p_key, p_ctrl, p_shift, p_meta);
|
emit keyPressed(p_key, p_ctrl, p_shift, p_meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VDocument::highlightTextAsync(const QString &p_text, int p_id, int p_timeStamp)
|
void VDocument::highlightTextAsync(const QString &p_text, int p_id, unsigned long long p_timeStamp)
|
||||||
{
|
{
|
||||||
emit requestHighlightText(p_text, p_id, p_timeStamp);
|
emit requestHighlightText(p_text, p_id, p_timeStamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VDocument::highlightTextCB(const QString &p_html, int p_id, int p_timeStamp)
|
void VDocument::highlightTextCB(const QString &p_html, int p_id, unsigned long long p_timeStamp)
|
||||||
{
|
{
|
||||||
emit textHighlighted(p_html, p_id, p_timeStamp);
|
emit textHighlighted(p_html, p_id, p_timeStamp);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ public:
|
|||||||
|
|
||||||
// Request to highlight a segment text.
|
// Request to highlight a segment text.
|
||||||
// Use p_id to identify the result.
|
// Use p_id to identify the result.
|
||||||
void highlightTextAsync(const QString &p_text, int p_id, int p_timeStamp);
|
void highlightTextAsync(const QString &p_text, int p_id, unsigned long long p_timeStamp);
|
||||||
|
|
||||||
// Request to convert @p_text to HTML.
|
// Request to convert @p_text to HTML.
|
||||||
void textToHtmlAsync(int p_identitifer,
|
void textToHtmlAsync(int p_identitifer,
|
||||||
@ -84,7 +84,7 @@ public slots:
|
|||||||
void keyPressEvent(int p_key, bool p_ctrl, bool p_shift, bool p_meta);
|
void keyPressEvent(int p_key, bool p_ctrl, bool p_shift, bool p_meta);
|
||||||
void updateText();
|
void updateText();
|
||||||
|
|
||||||
void highlightTextCB(const QString &p_html, int p_id, int p_timeStamp);
|
void highlightTextCB(const QString &p_html, int p_id, unsigned long long p_timeStamp);
|
||||||
|
|
||||||
void noticeReadyToHighlightText();
|
void noticeReadyToHighlightText();
|
||||||
|
|
||||||
@ -128,9 +128,9 @@ signals:
|
|||||||
|
|
||||||
void keyPressed(int p_key, bool p_ctrl, bool p_shift, bool p_meta);
|
void keyPressed(int p_key, bool p_ctrl, bool p_shift, bool p_meta);
|
||||||
|
|
||||||
void requestHighlightText(const QString &p_text, int p_id, int p_timeStamp);
|
void requestHighlightText(const QString &p_text, int p_id, unsigned long long p_timeStamp);
|
||||||
|
|
||||||
void textHighlighted(const QString &p_html, int p_id, int p_timeStamp);
|
void textHighlighted(const QString &p_html, int p_id, unsigned long long p_timeStamp);
|
||||||
|
|
||||||
void readyToHighlightText();
|
void readyToHighlightText();
|
||||||
|
|
||||||
|
@ -125,8 +125,9 @@ void VLivePreviewHelper::checkLang(const QString &p_lang,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VLivePreviewHelper::updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlocks)
|
void VLivePreviewHelper::updateCodeBlocks(TimeStamp p_timeStamp, const QVector<VCodeBlock> &p_codeBlocks)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(p_timeStamp);
|
||||||
if (!m_livePreviewEnabled && !m_inplacePreviewEnabled) {
|
if (!m_livePreviewEnabled && !m_inplacePreviewEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ public:
|
|||||||
bool isPreviewEnabled() const;
|
bool isPreviewEnabled() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlocks);
|
void updateCodeBlocks(TimeStamp p_timeStamp, const QVector<VCodeBlock> &p_codeBlocks);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void inplacePreviewCodeBlockUpdated(const QVector<QSharedPointer<VImageToPreview> > &p_images);
|
void inplacePreviewCodeBlockUpdated(const QVector<QSharedPointer<VImageToPreview> > &p_images);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include <QtWidgets>
|
#include <QtWidgets>
|
||||||
#include "vmdedit.h"
|
#include "vmdedit.h"
|
||||||
#include "hgmarkdownhighlighter.h"
|
#include "hgmarkdownhighlighter.h"
|
||||||
#include "vcodeblockhighlighthelper.h"
|
|
||||||
#include "vmdeditoperations.h"
|
#include "vmdeditoperations.h"
|
||||||
#include "vnote.h"
|
#include "vnote.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
@ -24,6 +23,9 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
|
|||||||
: VEdit(p_file, p_parent), m_mdHighlighter(NULL), m_freshEdit(true),
|
: VEdit(p_file, p_parent), m_mdHighlighter(NULL), m_freshEdit(true),
|
||||||
m_finishedAsyncJobs(c_numberOfAysncJobs)
|
m_finishedAsyncJobs(c_numberOfAysncJobs)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(p_type);
|
||||||
|
Q_UNUSED(p_vdoc);
|
||||||
|
|
||||||
V_ASSERT(p_file->getDocType() == DocType::Markdown);
|
V_ASSERT(p_file->getDocType() == DocType::Markdown);
|
||||||
|
|
||||||
setAcceptRichText(false);
|
setAcceptRichText(false);
|
||||||
@ -43,9 +45,6 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
|
|||||||
makeBlockVisible(textCursor().block());
|
makeBlockVisible(textCursor().block());
|
||||||
});
|
});
|
||||||
|
|
||||||
m_cbHighlighter = new VCodeBlockHighlightHelper(m_mdHighlighter, p_vdoc,
|
|
||||||
p_type);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
m_imagePreviewer = new VImagePreviewer(this, m_mdHighlighter);
|
m_imagePreviewer = new VImagePreviewer(this, m_mdHighlighter);
|
||||||
connect(m_mdHighlighter, &HGMarkdownHighlighter::imageLinksUpdated,
|
connect(m_mdHighlighter, &HGMarkdownHighlighter::imageLinksUpdated,
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
|
|
||||||
class HGMarkdownHighlighter;
|
class HGMarkdownHighlighter;
|
||||||
class VCodeBlockHighlightHelper;
|
|
||||||
class VDocument;
|
class VDocument;
|
||||||
|
|
||||||
class VMdEdit : public VEdit
|
class VMdEdit : public VEdit
|
||||||
@ -110,7 +109,6 @@ private:
|
|||||||
int indexOfCurrentHeader() const;
|
int indexOfCurrentHeader() const;
|
||||||
|
|
||||||
HGMarkdownHighlighter *m_mdHighlighter;
|
HGMarkdownHighlighter *m_mdHighlighter;
|
||||||
VCodeBlockHighlightHelper *m_cbHighlighter;
|
|
||||||
// VImagePreviewer *m_imagePreviewer;
|
// VImagePreviewer *m_imagePreviewer;
|
||||||
|
|
||||||
// Image links inserted while editing.
|
// Image links inserted while editing.
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include "vdocument.h"
|
#include "vdocument.h"
|
||||||
#include "utils/veditutils.h"
|
#include "utils/veditutils.h"
|
||||||
#include "vedittab.h"
|
#include "vedittab.h"
|
||||||
#include "hgmarkdownhighlighter.h"
|
#include "pegmarkdownhighlighter.h"
|
||||||
#include "vcodeblockhighlighthelper.h"
|
#include "vcodeblockhighlighthelper.h"
|
||||||
#include "vmdeditoperations.h"
|
#include "vmdeditoperations.h"
|
||||||
#include "vtableofcontent.h"
|
#include "vtableofcontent.h"
|
||||||
@ -35,7 +35,7 @@ VMdEditor::VMdEditor(VFile *p_file,
|
|||||||
QWidget *p_parent)
|
QWidget *p_parent)
|
||||||
: VTextEdit(p_parent),
|
: VTextEdit(p_parent),
|
||||||
VEditor(p_file, this),
|
VEditor(p_file, this),
|
||||||
m_mdHighlighter(NULL),
|
m_pegHighlighter(NULL),
|
||||||
m_freshEdit(true),
|
m_freshEdit(true),
|
||||||
m_textToHtmlDialog(NULL),
|
m_textToHtmlDialog(NULL),
|
||||||
m_zoomDelta(0),
|
m_zoomDelta(0),
|
||||||
@ -60,18 +60,17 @@ VMdEditor::VMdEditor(VFile *p_file,
|
|||||||
|
|
||||||
setReadOnly(true);
|
setReadOnly(true);
|
||||||
|
|
||||||
m_mdHighlighter = new HGMarkdownHighlighter(g_config->getMdHighlightingStyles(),
|
m_pegHighlighter = new PegMarkdownHighlighter(document());
|
||||||
g_config->getCodeBlockStyles(),
|
m_pegHighlighter->init(g_config->getMdHighlightingStyles(),
|
||||||
g_config->getMarkdownHighlightInterval(),
|
g_config->getCodeBlockStyles(),
|
||||||
document());
|
g_config->getEnableMathjax(),
|
||||||
m_mdHighlighter->setMathjaxEnabled(g_config->getEnableMathjax());
|
g_config->getMarkdownHighlightInterval());
|
||||||
|
connect(m_pegHighlighter, &PegMarkdownHighlighter::headersUpdated,
|
||||||
connect(m_mdHighlighter, &HGMarkdownHighlighter::headersUpdated,
|
|
||||||
this, &VMdEditor::updateHeaders);
|
this, &VMdEditor::updateHeaders);
|
||||||
|
|
||||||
// After highlight, the cursor may trun into non-visible. We should make it visible
|
// After highlight, the cursor may trun into non-visible. We should make it visible
|
||||||
// in this case.
|
// in this case.
|
||||||
connect(m_mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
|
connect(m_pegHighlighter, &PegMarkdownHighlighter::highlightCompleted,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
makeBlockVisible(textCursor().block());
|
makeBlockVisible(textCursor().block());
|
||||||
|
|
||||||
@ -81,15 +80,15 @@ VMdEditor::VMdEditor(VFile *p_file,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
m_cbHighlighter = new VCodeBlockHighlightHelper(m_mdHighlighter,
|
m_cbHighlighter = new VCodeBlockHighlightHelper(m_pegHighlighter,
|
||||||
p_doc,
|
p_doc,
|
||||||
p_type);
|
p_type);
|
||||||
|
|
||||||
m_previewMgr = new VPreviewManager(this, m_mdHighlighter);
|
m_previewMgr = new VPreviewManager(this, m_pegHighlighter);
|
||||||
connect(m_mdHighlighter, &HGMarkdownHighlighter::imageLinksUpdated,
|
connect(m_pegHighlighter, &PegMarkdownHighlighter::imageLinksUpdated,
|
||||||
m_previewMgr, &VPreviewManager::updateImageLinks);
|
m_previewMgr, &VPreviewManager::updateImageLinks);
|
||||||
connect(m_previewMgr, &VPreviewManager::requestUpdateImageLinks,
|
connect(m_previewMgr, &VPreviewManager::requestUpdateImageLinks,
|
||||||
m_mdHighlighter, &HGMarkdownHighlighter::updateHighlight);
|
m_pegHighlighter, &PegMarkdownHighlighter::updateHighlight);
|
||||||
|
|
||||||
m_editOps = new VMdEditOperations(this, m_file);
|
m_editOps = new VMdEditOperations(this, m_file);
|
||||||
connect(m_editOps, &VEditOperations::statusMessage,
|
connect(m_editOps, &VEditOperations::statusMessage,
|
||||||
@ -132,10 +131,10 @@ void VMdEditor::beginEdit()
|
|||||||
emit statusChanged();
|
emit statusChanged();
|
||||||
|
|
||||||
if (m_freshEdit) {
|
if (m_freshEdit) {
|
||||||
m_mdHighlighter->updateHighlight();
|
m_pegHighlighter->updateHighlight();
|
||||||
relayout();
|
relayout();
|
||||||
} else {
|
} else {
|
||||||
updateHeaders(m_mdHighlighter->getHeaderRegions());
|
updateHeaders(m_pegHighlighter->getHeaderRegions());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +169,7 @@ void VMdEditor::reloadFile()
|
|||||||
const QString &content = m_file->getContent();
|
const QString &content = m_file->getContent();
|
||||||
setPlainText(content);
|
setPlainText(content);
|
||||||
setModified(false);
|
setModified(false);
|
||||||
m_mdHighlighter->updateHighlightFast();
|
m_pegHighlighter->updateHighlightFast();
|
||||||
|
|
||||||
m_freshEdit = true;
|
m_freshEdit = true;
|
||||||
|
|
||||||
@ -461,7 +460,7 @@ static void insertSequenceToHeader(QTextCursor& p_cursor,
|
|||||||
|
|
||||||
void VMdEditor::updateHeaderSequenceByConfigChange()
|
void VMdEditor::updateHeaderSequenceByConfigChange()
|
||||||
{
|
{
|
||||||
updateHeadersHelper(m_mdHighlighter->getHeaderRegions(), true);
|
updateHeadersHelper(m_pegHighlighter->getHeaderRegions(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEditor::updateHeadersHelper(const QVector<VElementRegion> &p_headerRegions, bool p_configChanged)
|
void VMdEditor::updateHeadersHelper(const QVector<VElementRegion> &p_headerRegions, bool p_configChanged)
|
||||||
@ -493,8 +492,7 @@ void VMdEditor::updateHeadersHelper(const QVector<VElementRegion> &p_headerRegio
|
|||||||
<< block.text();
|
<< block.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((block.userState() == HighlightBlockState::Header)
|
if (headerReg.exactMatch(block.text())) {
|
||||||
&& headerReg.exactMatch(block.text())) {
|
|
||||||
int level = headerReg.cap(1).length();
|
int level = headerReg.cap(1).length();
|
||||||
VTableOfContentItem header(headerReg.cap(2).trimmed(),
|
VTableOfContentItem header(headerReg.cap(2).trimmed(),
|
||||||
level,
|
level,
|
||||||
@ -1199,7 +1197,7 @@ void VMdEditor::zoomPage(bool p_zoomIn, int p_range)
|
|||||||
|
|
||||||
m_zoomDelta += delta;
|
m_zoomDelta += delta;
|
||||||
|
|
||||||
QVector<HighlightingStyle> &styles = m_mdHighlighter->getHighlightingStyles();
|
QVector<HighlightingStyle> &styles = m_pegHighlighter->getStyles();
|
||||||
for (auto & it : styles) {
|
for (auto & it : styles) {
|
||||||
int size = it.format.fontPointSize();
|
int size = it.format.fontPointSize();
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
@ -1215,7 +1213,7 @@ void VMdEditor::zoomPage(bool p_zoomIn, int p_range)
|
|||||||
it.format.setFontPointSize(size);
|
it.format.setFontPointSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<QString, QTextCharFormat> &cbStyles = m_mdHighlighter->getCodeBlockStyles();
|
QHash<QString, QTextCharFormat> &cbStyles = m_pegHighlighter->getCodeBlockStyles();
|
||||||
for (auto it = cbStyles.begin(); it != cbStyles.end(); ++it) {
|
for (auto it = cbStyles.begin(); it != cbStyles.end(); ++it) {
|
||||||
int size = it.value().fontPointSize();
|
int size = it.value().fontPointSize();
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
@ -1231,7 +1229,7 @@ void VMdEditor::zoomPage(bool p_zoomIn, int p_range)
|
|||||||
it.value().setFontPointSize(size);
|
it.value().setFontPointSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_mdHighlighter->rehighlight();
|
m_pegHighlighter->rehighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEditor::initCopyAsMenu(QAction *p_before, QMenu *p_menu)
|
void VMdEditor::initCopyAsMenu(QAction *p_before, QMenu *p_menu)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
|
|
||||||
class HGMarkdownHighlighter;
|
class PegMarkdownHighlighter;
|
||||||
class VCodeBlockHighlightHelper;
|
class VCodeBlockHighlightHelper;
|
||||||
class VDocument;
|
class VDocument;
|
||||||
class VPreviewManager;
|
class VPreviewManager;
|
||||||
@ -72,7 +72,7 @@ public:
|
|||||||
|
|
||||||
void setEditTab(VEditTab *p_editTab);
|
void setEditTab(VEditTab *p_editTab);
|
||||||
|
|
||||||
HGMarkdownHighlighter *getMarkdownHighlighter() const;
|
PegMarkdownHighlighter *getMarkdownHighlighter() const;
|
||||||
|
|
||||||
VPreviewManager *getPreviewManager() const;
|
VPreviewManager *getPreviewManager() const;
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ private:
|
|||||||
|
|
||||||
void insertImageLink(const QString &p_text, const QString &p_url);
|
void insertImageLink(const QString &p_text, const QString &p_url);
|
||||||
|
|
||||||
HGMarkdownHighlighter *m_mdHighlighter;
|
PegMarkdownHighlighter *m_pegHighlighter;
|
||||||
|
|
||||||
VCodeBlockHighlightHelper *m_cbHighlighter;
|
VCodeBlockHighlightHelper *m_cbHighlighter;
|
||||||
|
|
||||||
@ -282,9 +282,9 @@ private:
|
|||||||
int m_copyTimeStamp;
|
int m_copyTimeStamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline HGMarkdownHighlighter *VMdEditor::getMarkdownHighlighter() const
|
inline PegMarkdownHighlighter *VMdEditor::getMarkdownHighlighter() const
|
||||||
{
|
{
|
||||||
return m_mdHighlighter;
|
return m_pegHighlighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline VPreviewManager *VMdEditor::getPreviewManager() const
|
inline VPreviewManager *VMdEditor::getPreviewManager() const
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include "vnote.h"
|
#include "vnote.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
#include "vpreviewpage.h"
|
#include "vpreviewpage.h"
|
||||||
#include "hgmarkdownhighlighter.h"
|
#include "pegmarkdownhighlighter.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vmarkdownconverter.h"
|
#include "vmarkdownconverter.h"
|
||||||
#include "vnotebook.h"
|
#include "vnotebook.h"
|
||||||
@ -535,7 +535,7 @@ void VMdTab::setupMarkdownEditor()
|
|||||||
m_splitter->insertWidget(0, m_editor);
|
m_splitter->insertWidget(0, m_editor);
|
||||||
|
|
||||||
m_livePreviewHelper = new VLivePreviewHelper(m_editor, m_document, this);
|
m_livePreviewHelper = new VLivePreviewHelper(m_editor, m_document, this);
|
||||||
connect(m_editor->getMarkdownHighlighter(), &HGMarkdownHighlighter::codeBlocksUpdated,
|
connect(m_editor->getMarkdownHighlighter(), &PegMarkdownHighlighter::codeBlocksUpdated,
|
||||||
m_livePreviewHelper, &VLivePreviewHelper::updateCodeBlocks);
|
m_livePreviewHelper, &VLivePreviewHelper::updateCodeBlocks);
|
||||||
connect(m_editor->getPreviewManager(), &VPreviewManager::previewEnabledChanged,
|
connect(m_editor->getPreviewManager(), &VPreviewManager::previewEnabledChanged,
|
||||||
m_livePreviewHelper, &VLivePreviewHelper::setInplacePreviewEnabled);
|
m_livePreviewHelper, &VLivePreviewHelper::setInplacePreviewEnabled);
|
||||||
@ -546,7 +546,7 @@ void VMdTab::setupMarkdownEditor()
|
|||||||
m_livePreviewHelper->setInplacePreviewEnabled(m_editor->getPreviewManager()->isPreviewEnabled());
|
m_livePreviewHelper->setInplacePreviewEnabled(m_editor->getPreviewManager()->isPreviewEnabled());
|
||||||
|
|
||||||
m_mathjaxPreviewHelper = new VMathJaxInplacePreviewHelper(m_editor, m_document, this);
|
m_mathjaxPreviewHelper = new VMathJaxInplacePreviewHelper(m_editor, m_document, this);
|
||||||
connect(m_editor->getMarkdownHighlighter(), &HGMarkdownHighlighter::mathjaxBlocksUpdated,
|
connect(m_editor->getMarkdownHighlighter(), &PegMarkdownHighlighter::mathjaxBlocksUpdated,
|
||||||
m_mathjaxPreviewHelper, &VMathJaxInplacePreviewHelper::updateMathjaxBlocks);
|
m_mathjaxPreviewHelper, &VMathJaxInplacePreviewHelper::updateMathjaxBlocks);
|
||||||
connect(m_editor->getPreviewManager(), &VPreviewManager::previewEnabledChanged,
|
connect(m_editor->getPreviewManager(), &VPreviewManager::previewEnabledChanged,
|
||||||
m_mathjaxPreviewHelper, &VMathJaxInplacePreviewHelper::setEnabled);
|
m_mathjaxPreviewHelper, &VMathJaxInplacePreviewHelper::setEnabled);
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
#include "vdownloader.h"
|
#include "vdownloader.h"
|
||||||
#include "hgmarkdownhighlighter.h"
|
#include "pegmarkdownhighlighter.h"
|
||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
VPreviewManager::VPreviewManager(VMdEditor *p_editor, HGMarkdownHighlighter *p_highlighter)
|
VPreviewManager::VPreviewManager(VMdEditor *p_editor, PegMarkdownHighlighter *p_highlighter)
|
||||||
: QObject(p_editor),
|
: QObject(p_editor),
|
||||||
m_editor(p_editor),
|
m_editor(p_editor),
|
||||||
m_document(p_editor->document()),
|
m_document(p_editor->document()),
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
#include "hgmarkdownhighlighter.h"
|
#include "markdownhighlighterdata.h"
|
||||||
#include "vmdeditor.h"
|
#include "vmdeditor.h"
|
||||||
#include "vtextblockdata.h"
|
#include "vtextblockdata.h"
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ class VPreviewManager : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
VPreviewManager(VMdEditor *p_editor, HGMarkdownHighlighter *p_highlighter);
|
VPreviewManager(VMdEditor *p_editor, PegMarkdownHighlighter *p_highlighter);
|
||||||
|
|
||||||
void setPreviewEnabled(bool p_enabled);
|
void setPreviewEnabled(bool p_enabled);
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ private:
|
|||||||
|
|
||||||
QTextDocument *m_document;
|
QTextDocument *m_document;
|
||||||
|
|
||||||
HGMarkdownHighlighter *m_highlighter;
|
PegMarkdownHighlighter *m_highlighter;
|
||||||
|
|
||||||
VDownloader *m_downloader;
|
VDownloader *m_downloader;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user