mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
change HGMarkdownHighlighter to use QSyntaxHighlighter
Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
parent
4a1c5e0a91
commit
29609f0b65
@ -11,32 +11,9 @@
|
|||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
#include "hgmarkdownhighlighter.h"
|
#include "hgmarkdownhighlighter.h"
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG
|
const int HGMarkdownHighlighter::initCapacity = 1024;
|
||||||
#define V_HIGHLIGHT_DEBUG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const unsigned int WorkerThread::initCapacity = 1024;
|
void HGMarkdownHighlighter::resizeBuffer(int newCap)
|
||||||
|
|
||||||
WorkerThread::WorkerThread()
|
|
||||||
: QThread(NULL), content(NULL), capacity(0), result(NULL)
|
|
||||||
{
|
|
||||||
resizeBuffer(initCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkerThread::~WorkerThread()
|
|
||||||
{
|
|
||||||
if (result) {
|
|
||||||
pmh_free_elements(result);
|
|
||||||
result = NULL;
|
|
||||||
}
|
|
||||||
if (content) {
|
|
||||||
delete [] content;
|
|
||||||
capacity = 0;
|
|
||||||
content = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WorkerThread::resizeBuffer(unsigned int newCap)
|
|
||||||
{
|
{
|
||||||
if (newCap == capacity) {
|
if (newCap == capacity) {
|
||||||
return;
|
return;
|
||||||
@ -49,74 +26,59 @@ void WorkerThread::resizeBuffer(unsigned int newCap)
|
|||||||
content = new char [capacity];
|
content = new char [capacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkerThread::prepareAndStart(const char *data)
|
|
||||||
{
|
|
||||||
Q_ASSERT(data);
|
|
||||||
unsigned int len = strlen(data);
|
|
||||||
if (len >= capacity) {
|
|
||||||
resizeBuffer(qMax(2 * capacity, len + 1));
|
|
||||||
}
|
|
||||||
Q_ASSERT(content);
|
|
||||||
memcpy(content, data, len);
|
|
||||||
content[len] = '\0';
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
pmh_free_elements(result);
|
|
||||||
result = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
pmh_element** WorkerThread::retriveResult()
|
|
||||||
{
|
|
||||||
Q_ASSERT(result);
|
|
||||||
pmh_element** ret = result;
|
|
||||||
result = NULL;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WorkerThread::run()
|
|
||||||
{
|
|
||||||
if (content == NULL)
|
|
||||||
return;
|
|
||||||
Q_ASSERT(!result);
|
|
||||||
pmh_markdown_to_elements(content, pmh_EXT_NONE, &result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Will be freeed by parent automatically
|
// Will be freeed by parent automatically
|
||||||
HGMarkdownHighlighter::HGMarkdownHighlighter(const QVector<HighlightingStyle> &styles,
|
HGMarkdownHighlighter::HGMarkdownHighlighter(const QVector<HighlightingStyle> &styles, int waitInterval,
|
||||||
QTextDocument *parent,
|
QTextDocument *parent)
|
||||||
int aWaitInterval) : QObject(parent)
|
: QSyntaxHighlighter(parent), parsing(0),
|
||||||
|
waitInterval(waitInterval), content(NULL), capacity(0), result(NULL)
|
||||||
{
|
{
|
||||||
workerThread = new WorkerThread();
|
codeBlockStartExp = QRegExp("^```");
|
||||||
cached_elements = NULL;
|
codeBlockEndExp = QRegExp("^```$");
|
||||||
waitInterval = aWaitInterval;
|
codeBlockFormat.setForeground(QBrush(Qt::darkYellow));
|
||||||
|
for (int index = 0; index < styles.size(); ++index) {
|
||||||
|
if (styles[index].type == pmh_VERBATIM) {
|
||||||
|
codeBlockFormat = styles[index].format;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeBuffer(initCapacity);
|
||||||
setStyles(styles);
|
setStyles(styles);
|
||||||
|
document = parent;
|
||||||
timer = new QTimer(this);
|
timer = new QTimer(this);
|
||||||
timer->setSingleShot(true);
|
timer->setSingleShot(true);
|
||||||
timer->setInterval(aWaitInterval);
|
timer->setInterval(this->waitInterval);
|
||||||
connect(timer, SIGNAL(timeout()), this, SLOT(timerTimeout()));
|
connect(timer, &QTimer::timeout, this, &HGMarkdownHighlighter::timerTimeout);
|
||||||
document = parent;
|
connect(document, &QTextDocument::contentsChange,
|
||||||
connect(document, SIGNAL(contentsChange(int,int,int)),
|
this, &HGMarkdownHighlighter::handleContentChange);
|
||||||
this, SLOT(handleContentsChange(int,int,int)));
|
|
||||||
connect(workerThread, SIGNAL(finished()), this, SLOT(threadFinished()));
|
|
||||||
this->parse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HGMarkdownHighlighter::~HGMarkdownHighlighter()
|
HGMarkdownHighlighter::~HGMarkdownHighlighter()
|
||||||
{
|
{
|
||||||
if (workerThread) {
|
if (result) {
|
||||||
if (workerThread->isRunning()) {
|
pmh_free_elements(result);
|
||||||
workerThread->wait();
|
result = NULL;
|
||||||
}
|
}
|
||||||
delete workerThread;
|
if (content) {
|
||||||
workerThread = NULL;
|
delete [] content;
|
||||||
|
capacity = 0;
|
||||||
|
content = NULL;
|
||||||
}
|
}
|
||||||
if (cached_elements) {
|
|
||||||
pmh_free_elements(cached_elements);
|
|
||||||
cached_elements = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HGMarkdownHighlighter::highlightBlock(const QString &text)
|
||||||
|
{
|
||||||
|
int blockNum = currentBlock().blockNumber();
|
||||||
|
if (!parsing && blockHighlights.size() > blockNum) {
|
||||||
|
QVector<HLUnit> &units = blockHighlights[blockNum];
|
||||||
|
for (int i = 0; i < units.size(); ++i) {
|
||||||
|
// TODO: merge two format within the same range
|
||||||
|
const HLUnit& unit = units[i];
|
||||||
|
setFormat(unit.start, unit.length, highlightingStyles[unit.styleIndex].format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCurrentBlockState(0);
|
||||||
|
highlightCodeBlock(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HGMarkdownHighlighter::setStyles(const QVector<HighlightingStyle> &styles)
|
void HGMarkdownHighlighter::setStyles(const QVector<HighlightingStyle> &styles)
|
||||||
@ -124,174 +86,148 @@ void HGMarkdownHighlighter::setStyles(const QVector<HighlightingStyle> &styles)
|
|||||||
this->highlightingStyles = styles;
|
this->highlightingStyles = styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HGMarkdownHighlighter::clearFormatting()
|
void HGMarkdownHighlighter::initBlockHighlightFromResult(int nrBlocks)
|
||||||
{
|
{
|
||||||
QTextBlock block = document->firstBlock();
|
blockHighlights.resize(nrBlocks);
|
||||||
while (block.isValid()) {
|
for (int i = 0; i < blockHighlights.size(); ++i) {
|
||||||
block.layout()->clearAdditionalFormats();
|
blockHighlights[i].clear();
|
||||||
block = block.next();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HGMarkdownHighlighter::highlightOneRegion(const HighlightingStyle &style,
|
if (!result) {
|
||||||
unsigned long pos, unsigned long end, bool clearBeforeHighlight)
|
|
||||||
{
|
|
||||||
// "The QTextLayout object can only be modified from the
|
|
||||||
// documentChanged implementation of a QAbstractTextDocumentLayout
|
|
||||||
// subclass. Any changes applied from the outside cause undefined
|
|
||||||
// behavior." -- we are breaking this rule here. There might be
|
|
||||||
// a better (more correct) way to do this.
|
|
||||||
int startBlockNum = document->findBlock(pos).blockNumber();
|
|
||||||
int endBlockNum = document->findBlock(end).blockNumber();
|
|
||||||
for (int i = startBlockNum; i <= endBlockNum; ++i)
|
|
||||||
{
|
|
||||||
QTextBlock block = document->findBlockByNumber(i);
|
|
||||||
|
|
||||||
QTextLayout *layout = block.layout();
|
|
||||||
if (clearBeforeHighlight) {
|
|
||||||
layout->clearFormats();
|
|
||||||
}
|
|
||||||
QVector<QTextLayout::FormatRange> list = layout->formats();
|
|
||||||
int blockpos = block.position();
|
|
||||||
QTextLayout::FormatRange r;
|
|
||||||
r.format = style.format;
|
|
||||||
|
|
||||||
if (i == startBlockNum) {
|
|
||||||
r.start = pos - blockpos;
|
|
||||||
r.length = (startBlockNum == endBlockNum)
|
|
||||||
? end - pos
|
|
||||||
: block.length() - r.start;
|
|
||||||
} else if (i == endBlockNum) {
|
|
||||||
r.start = 0;
|
|
||||||
r.length = end - blockpos;
|
|
||||||
} else {
|
|
||||||
r.start = 0;
|
|
||||||
r.length = block.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
list.append(r);
|
|
||||||
layout->setFormats(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HGMarkdownHighlighter::highlight()
|
|
||||||
{
|
|
||||||
if (cached_elements == NULL) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (highlightingStyles.isEmpty()) {
|
|
||||||
qWarning() << "error: HighlightingStyles is not set";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->clearFormatting();
|
|
||||||
|
|
||||||
// To make sure content is not changed by highlight operations.
|
|
||||||
// May be resource-consuming. Can be removed if no need.
|
|
||||||
#ifdef V_HIGHLIGHT_DEBUG
|
|
||||||
QString oriContent = document->toPlainText();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int i = 0; i < highlightingStyles.size(); i++)
|
for (int i = 0; i < highlightingStyles.size(); i++)
|
||||||
{
|
{
|
||||||
const HighlightingStyle &style = highlightingStyles[i];
|
const HighlightingStyle &style = highlightingStyles[i];
|
||||||
pmh_element *elem_cursor = cached_elements[style.type];
|
pmh_element *elem_cursor = result[style.type];
|
||||||
while (elem_cursor != NULL)
|
while (elem_cursor != NULL)
|
||||||
{
|
{
|
||||||
if (elem_cursor->end <= elem_cursor->pos) {
|
if (elem_cursor->end <= elem_cursor->pos) {
|
||||||
elem_cursor = elem_cursor->next;
|
elem_cursor = elem_cursor->next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
highlightOneRegion(style, elem_cursor->pos, elem_cursor->end);
|
initBlockHighlihgtOne(elem_cursor->pos, elem_cursor->end, i);
|
||||||
elem_cursor = elem_cursor->next;
|
elem_cursor = elem_cursor->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightCodeBlock();
|
pmh_free_elements(result);
|
||||||
|
result = NULL;
|
||||||
document->markContentsDirty(0, document->characterCount());
|
|
||||||
|
|
||||||
#ifdef V_HIGHLIGHT_DEBUG
|
|
||||||
if (oriContent != document->toPlainText()) {
|
|
||||||
qWarning() << "warning: content was changed before and after highlighting";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HGMarkdownHighlighter::highlightCodeBlock()
|
void HGMarkdownHighlighter::initBlockHighlihgtOne(unsigned long pos, unsigned long end, int styleIndex)
|
||||||
{
|
{
|
||||||
QRegExp codeRegStart("^```");
|
int startBlockNum = document->findBlock(pos).blockNumber();
|
||||||
QRegExp codeRegEnd("^```$");
|
int endBlockNum = document->findBlock(end).blockNumber();
|
||||||
|
for (int i = startBlockNum; i <= endBlockNum; ++i)
|
||||||
|
{
|
||||||
|
QTextBlock block = document->findBlockByNumber(i);
|
||||||
|
int blockStartPos = block.position();
|
||||||
|
HLUnit unit;
|
||||||
|
if (i == startBlockNum) {
|
||||||
|
unit.start = pos - blockStartPos;
|
||||||
|
unit.length = (startBlockNum == endBlockNum) ?
|
||||||
|
(end - pos) : (block.length() - unit.start);
|
||||||
|
} else if (i == endBlockNum) {
|
||||||
|
unit.start = 0;
|
||||||
|
unit.length = end - blockStartPos;
|
||||||
|
} else {
|
||||||
|
unit.start = 0;
|
||||||
|
unit.length = block.length();
|
||||||
|
}
|
||||||
|
unit.styleIndex = styleIndex;
|
||||||
|
|
||||||
HighlightingStyle style;
|
blockHighlights[i].append(unit);
|
||||||
int index = 0;
|
|
||||||
for (index = 0; index < highlightingStyles.size(); ++index) {
|
|
||||||
if (highlightingStyles[index].type == pmh_VERBATIM) {
|
|
||||||
style = highlightingStyles[index];
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (index == highlightingStyles.size()) {
|
|
||||||
style.type = pmh_VERBATIM;
|
void HGMarkdownHighlighter::highlightCodeBlock(const QString &text)
|
||||||
style.format.setForeground(QBrush(Qt::darkYellow));
|
{
|
||||||
|
int nextIndex = 0;
|
||||||
|
int startIndex = 0;
|
||||||
|
if (previousBlockState() != 1) {
|
||||||
|
startIndex = codeBlockStartExp.indexIn(text);
|
||||||
|
if (startIndex >= 0) {
|
||||||
|
nextIndex = startIndex + codeBlockStartExp.matchedLength();
|
||||||
|
} else {
|
||||||
|
nextIndex = -1;
|
||||||
}
|
}
|
||||||
int pos = 0;
|
|
||||||
while (true) {
|
|
||||||
QTextCursor startCursor = document->find(codeRegStart, pos);
|
|
||||||
if (!startCursor.hasSelection()) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
pos = startCursor.selectionEnd();
|
|
||||||
QTextCursor endCursor = document->find(codeRegEnd, pos);
|
while (nextIndex >= 0) {
|
||||||
if (!endCursor.hasSelection()) {
|
int endIndex = codeBlockEndExp.indexIn(text, nextIndex);
|
||||||
break;
|
int codeBlockLength;
|
||||||
|
if (endIndex == -1) {
|
||||||
|
setCurrentBlockState(1);
|
||||||
|
codeBlockLength = text.length() - startIndex;
|
||||||
|
} else {
|
||||||
|
codeBlockLength = endIndex - startIndex + codeBlockEndExp.matchedLength();
|
||||||
|
}
|
||||||
|
setFormat(startIndex, codeBlockLength, codeBlockFormat);
|
||||||
|
startIndex = codeBlockStartExp.indexIn(text, startIndex + codeBlockLength);
|
||||||
|
if (startIndex >= 0) {
|
||||||
|
nextIndex = startIndex + codeBlockStartExp.matchedLength();
|
||||||
|
} else {
|
||||||
|
nextIndex = -1;
|
||||||
}
|
}
|
||||||
pos = endCursor.selectionEnd();
|
|
||||||
highlightOneRegion(style, startCursor.selectionStart(), endCursor.selectionEnd(),
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HGMarkdownHighlighter::parse()
|
void HGMarkdownHighlighter::parse()
|
||||||
{
|
{
|
||||||
if (workerThread->isRunning()) {
|
if (!parsing.testAndSetRelaxed(0, 1)) {
|
||||||
parsePending = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString content = document->toPlainText();
|
int nrBlocks = document->blockCount();
|
||||||
QByteArray ba = content.toUtf8();
|
parseInternal();
|
||||||
parsePending = false;
|
|
||||||
workerThread->prepareAndStart((const char *)ba.data());
|
if (highlightingStyles.isEmpty()) {
|
||||||
|
qWarning() << "error: HighlightingStyles is not set";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initBlockHighlightFromResult(nrBlocks);
|
||||||
|
parsing.store(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HGMarkdownHighlighter::threadFinished()
|
void HGMarkdownHighlighter::parseInternal()
|
||||||
{
|
{
|
||||||
if (parsePending) {
|
QString text = document->toPlainText();
|
||||||
this->parse();
|
QByteArray ba = text.toUtf8();
|
||||||
|
const char *data = (const char *)ba.data();
|
||||||
|
int len = ba.size();
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
pmh_free_elements(result);
|
||||||
|
result = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
return;
|
return;
|
||||||
|
} else if (len >= capacity) {
|
||||||
|
resizeBuffer(qMax(2 * capacity, len + 1));
|
||||||
|
} else if (len < (capacity >> 2)) {
|
||||||
|
resizeBuffer(qMax(capacity >> 1, len + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cached_elements != NULL) {
|
memcpy(content, data, len);
|
||||||
pmh_free_elements(cached_elements);
|
content[len] = '\0';
|
||||||
}
|
|
||||||
cached_elements = workerThread->retriveResult();
|
pmh_markdown_to_elements(content, pmh_EXT_NONE, &result);
|
||||||
this->highlight();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HGMarkdownHighlighter::handleContentsChange(int position, int charsRemoved,
|
void HGMarkdownHighlighter::handleContentChange(int position, int charsRemoved, int charsAdded)
|
||||||
int charsAdded)
|
|
||||||
{
|
{
|
||||||
if (charsRemoved == 0 && charsAdded == 0)
|
if (charsRemoved == 0 && charsAdded == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
timer->stop();
|
timer->stop();
|
||||||
timer->start();
|
timer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HGMarkdownHighlighter::timerTimeout()
|
void HGMarkdownHighlighter::timerTimeout()
|
||||||
{
|
{
|
||||||
this->parse();
|
parse();
|
||||||
|
rehighlight();
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
#define HGMARKDOWNHIGHLIGHTER_H
|
#define HGMARKDOWNHIGHLIGHTER_H
|
||||||
|
|
||||||
#include <QTextCharFormat>
|
#include <QTextCharFormat>
|
||||||
#include <QThread>
|
#include <QSyntaxHighlighter>
|
||||||
|
#include <QAtomicInt>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "utils/peg-highlight/pmh_parser.h"
|
#include "utils/peg-highlight/pmh_parser.h"
|
||||||
@ -21,62 +22,65 @@ QT_BEGIN_NAMESPACE
|
|||||||
class QTextDocument;
|
class QTextDocument;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
class WorkerThread : public QThread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
WorkerThread();
|
|
||||||
~WorkerThread();
|
|
||||||
void prepareAndStart(const char *data);
|
|
||||||
pmh_element** retriveResult();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void run();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void resizeBuffer(unsigned int newCap);
|
|
||||||
|
|
||||||
char *content;
|
|
||||||
unsigned int capacity;
|
|
||||||
pmh_element **result;
|
|
||||||
static const unsigned int initCapacity;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HighlightingStyle
|
struct HighlightingStyle
|
||||||
{
|
{
|
||||||
pmh_element_type type;
|
pmh_element_type type;
|
||||||
QTextCharFormat format;
|
QTextCharFormat format;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HGMarkdownHighlighter : public QObject
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HGMarkdownHighlighter : public QSyntaxHighlighter
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HGMarkdownHighlighter(const QVector<HighlightingStyle> &styles,
|
HGMarkdownHighlighter(const QVector<HighlightingStyle> &styles, int waitInterval,
|
||||||
QTextDocument *parent = 0, int aWaitInterval = 2000);
|
QTextDocument *parent = 0);
|
||||||
~HGMarkdownHighlighter();
|
~HGMarkdownHighlighter();
|
||||||
void setStyles(const QVector<HighlightingStyle> &styles);
|
void setStyles(const QVector<HighlightingStyle> &styles);
|
||||||
int waitInterval;
|
|
||||||
|
protected:
|
||||||
|
void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleContentsChange(int position, int charsRemoved, int charsAdded);
|
void handleContentChange(int position, int charsRemoved, int charsAdded);
|
||||||
void threadFinished();
|
|
||||||
void timerTimeout();
|
void timerTimeout();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTimer *timer;
|
QRegExp codeBlockStartExp;
|
||||||
QTextDocument *document;
|
QRegExp codeBlockEndExp;
|
||||||
WorkerThread *workerThread;
|
QTextCharFormat codeBlockFormat;
|
||||||
bool parsePending;
|
|
||||||
pmh_element **cached_elements;
|
|
||||||
QVector<HighlightingStyle> highlightingStyles;
|
|
||||||
|
|
||||||
void clearFormatting();
|
QTextDocument *document;
|
||||||
void highlight();
|
QVector<HighlightingStyle> highlightingStyles;
|
||||||
void highlightOneRegion(const HighlightingStyle &style, unsigned long pos,
|
QVector<QVector<HLUnit> > blockHighlights;
|
||||||
unsigned long end, bool clearBeforeHighlight = false);
|
QAtomicInt parsing;
|
||||||
void highlightCodeBlock();
|
QTimer *timer;
|
||||||
|
int waitInterval;
|
||||||
|
|
||||||
|
char *content;
|
||||||
|
int capacity;
|
||||||
|
pmh_element **result;
|
||||||
|
static const int initCapacity;
|
||||||
|
void resizeBuffer(int newCap);
|
||||||
|
|
||||||
|
void highlightCodeBlock(const QString &text);
|
||||||
void parse();
|
void parse();
|
||||||
|
void parseInternal();
|
||||||
|
void initBlockHighlightFromResult(int nrBlocks);
|
||||||
|
void initBlockHighlihgtOne(unsigned long pos, unsigned long end,
|
||||||
|
int styleIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,16 +2,20 @@
|
|||||||
#include "vedit.h"
|
#include "vedit.h"
|
||||||
#include "vnote.h"
|
#include "vnote.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
|
#include "hgmarkdownhighlighter.h"
|
||||||
|
|
||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
|
|
||||||
VEdit::VEdit(VNoteFile *noteFile, QWidget *parent)
|
VEdit::VEdit(VNoteFile *noteFile, QWidget *parent)
|
||||||
: QTextEdit(parent), noteFile(noteFile)
|
: QTextEdit(parent), noteFile(noteFile), mdHighlighter(NULL)
|
||||||
{
|
{
|
||||||
if (noteFile->docType == DocType::Markdown) {
|
if (noteFile->docType == DocType::Markdown) {
|
||||||
setPalette(vconfig.getMdEditPalette());
|
setPalette(vconfig.getMdEditPalette());
|
||||||
setFont(vconfig.getMdEditFont());
|
setFont(vconfig.getMdEditFont());
|
||||||
setAcceptRichText(false);
|
setAcceptRichText(false);
|
||||||
|
|
||||||
|
mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(),
|
||||||
|
500, document());
|
||||||
} else {
|
} else {
|
||||||
setFont(vconfig.getBaseEditFont());
|
setFont(vconfig.getBaseEditFont());
|
||||||
setAutoFormatting(QTextEdit::AutoBulletList);
|
setAutoFormatting(QTextEdit::AutoBulletList);
|
||||||
|
3
vedit.h
3
vedit.h
@ -6,6 +6,8 @@
|
|||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
#include "vnotefile.h"
|
#include "vnotefile.h"
|
||||||
|
|
||||||
|
class HGMarkdownHighlighter;
|
||||||
|
|
||||||
class VEdit : public QTextEdit
|
class VEdit : public QTextEdit
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -28,6 +30,7 @@ public slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
VNoteFile *noteFile;
|
VNoteFile *noteFile;
|
||||||
|
HGMarkdownHighlighter *mdHighlighter;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VEDIT_H
|
#endif // VEDIT_H
|
||||||
|
@ -22,7 +22,6 @@ VEditor::VEditor(const QString &path, const QString &name, bool modifiable,
|
|||||||
noteFile = new VNoteFile(path, name, fileText, docType, modifiable);
|
noteFile = new VNoteFile(path, name, fileText, docType, modifiable);
|
||||||
|
|
||||||
isEditMode = false;
|
isEditMode = false;
|
||||||
mdHighlighter = NULL;
|
|
||||||
|
|
||||||
setupUI();
|
setupUI();
|
||||||
|
|
||||||
@ -45,9 +44,6 @@ void VEditor::setupUI()
|
|||||||
case DocType::Markdown:
|
case DocType::Markdown:
|
||||||
setupMarkdownPreview();
|
setupMarkdownPreview();
|
||||||
textBrowser = NULL;
|
textBrowser = NULL;
|
||||||
|
|
||||||
mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(),
|
|
||||||
textEditor->document(), 500);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DocType::Html:
|
case DocType::Html:
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
class QTextBrowser;
|
class QTextBrowser;
|
||||||
class VEdit;
|
class VEdit;
|
||||||
class QWebEngineView;
|
class QWebEngineView;
|
||||||
class HGMarkdownHighlighter;
|
|
||||||
|
|
||||||
class VEditor : public QStackedWidget
|
class VEditor : public QStackedWidget
|
||||||
{
|
{
|
||||||
@ -39,7 +38,6 @@ private:
|
|||||||
VEdit *textEditor;
|
VEdit *textEditor;
|
||||||
QWebEngineView *webPreviewer;
|
QWebEngineView *webPreviewer;
|
||||||
VDocument document;
|
VDocument document;
|
||||||
HGMarkdownHighlighter *mdHighlighter;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VEDITOR_H
|
#endif // VEDITOR_H
|
||||||
|
@ -115,15 +115,17 @@ void VStyleParser::parseMarkdownStyle(const QString &styleStr)
|
|||||||
|
|
||||||
QVector<HighlightingStyle> VStyleParser::fetchMarkdownStyles(const QFont &baseFont) const
|
QVector<HighlightingStyle> VStyleParser::fetchMarkdownStyles(const QFont &baseFont) const
|
||||||
{
|
{
|
||||||
QVector<HighlightingStyle> styles(pmh_NUM_LANG_TYPES);
|
QVector<HighlightingStyle> styles;
|
||||||
|
|
||||||
for (int i = 0; i < pmh_NUM_LANG_TYPES; ++i) {
|
for (int i = 0; i < pmh_NUM_LANG_TYPES; ++i) {
|
||||||
pmh_style_attribute *attr = markdownStyles->element_styles[i];
|
pmh_style_attribute *attr = markdownStyles->element_styles[i];
|
||||||
if (!attr) {
|
if (!attr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
styles[i].type = attr->lang_element_type;
|
HighlightingStyle style;
|
||||||
styles[i].format = QTextCharFormatFromAttrs(attr, baseFont);
|
style.type = attr->lang_element_type;
|
||||||
|
style.format = QTextCharFormatFromAttrs(attr, baseFont);
|
||||||
|
styles.append(style);
|
||||||
}
|
}
|
||||||
return styles;
|
return styles;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user