clean up HGMarkdownHighlighter

Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2016-10-12 21:05:41 +08:00
parent 22bf96c6e3
commit 33f62915e1
3 changed files with 125 additions and 31 deletions

View File

@ -8,26 +8,88 @@
*/ */
#include <QtGui> #include <QtGui>
#include <QtDebug>
#include "hgmarkdownhighlighter.h" #include "hgmarkdownhighlighter.h"
#ifndef QT_NO_DEBUG
#define V_HIGHLIGHT_DEBUG
#endif
const int WorkerThread::initCapacity = 1024;
WorkerThread::WorkerThread()
: QThread(NULL), content(NULL), result(NULL),
capacity(0)
{
resizeBuffer(initCapacity);
}
WorkerThread::~WorkerThread() WorkerThread::~WorkerThread()
{ {
if (result != NULL) if (result) {
pmh_free_elements(result); pmh_free_elements(result);
free(content); result = NULL;
} }
if (content) {
delete [] content;
capacity = 0;
content = NULL;
}
}
void WorkerThread::resizeBuffer(int newCap)
{
if (newCap == capacity) {
return;
}
if (capacity > 0) {
Q_ASSERT(content);
delete [] content;
}
capacity = newCap;
content = new char [capacity];
}
void WorkerThread::prepareAndStart(const char *data)
{
Q_ASSERT(data);
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() void WorkerThread::run()
{ {
if (content == NULL) if (content == NULL)
return; return;
Q_ASSERT(!result);
pmh_markdown_to_elements(content, pmh_EXT_NONE, &result); pmh_markdown_to_elements(content, pmh_EXT_NONE, &result);
} }
// Will be freeed by parent automatically
HGMarkdownHighlighter::HGMarkdownHighlighter(QTextDocument *parent, HGMarkdownHighlighter::HGMarkdownHighlighter(QTextDocument *parent,
int aWaitInterval) : QObject(parent) int aWaitInterval) : QObject(parent)
{ {
highlightingStyles = NULL; workerThread = new WorkerThread();
workerThread = NULL;
cached_elements = NULL; cached_elements = NULL;
waitInterval = aWaitInterval; waitInterval = aWaitInterval;
timer = new QTimer(this); timer = new QTimer(this);
@ -37,20 +99,36 @@ HGMarkdownHighlighter::HGMarkdownHighlighter(QTextDocument *parent,
document = parent; document = parent;
connect(document, SIGNAL(contentsChange(int,int,int)), connect(document, SIGNAL(contentsChange(int,int,int)),
this, SLOT(handleContentsChange(int,int,int))); this, SLOT(handleContentsChange(int,int,int)));
connect(workerThread, SIGNAL(finished()), this, SLOT(threadFinished()));
this->parse(); this->parse();
} }
void HGMarkdownHighlighter::setStyles(QVector<HighlightingStyle> &styles) HGMarkdownHighlighter::~HGMarkdownHighlighter()
{ {
this->highlightingStyles = &styles; if (workerThread) {
if (workerThread->isRunning()) {
workerThread->wait();
}
delete workerThread;
workerThread = NULL;
}
if (cached_elements) {
pmh_free_elements(cached_elements);
cached_elements = NULL;
}
} }
#define STY(type, format) styles->append((HighlightingStyle){type, format}) void HGMarkdownHighlighter::setStyles(const QVector<HighlightingStyle> &styles)
{
this->highlightingStyles = styles;
}
#define STY(type, format) styles.append({type, format})
void HGMarkdownHighlighter::setDefaultStyles() void HGMarkdownHighlighter::setDefaultStyles()
{ {
QVector<HighlightingStyle> *styles = new QVector<HighlightingStyle>(); QVector<HighlightingStyle> &styles = this->highlightingStyles;
styles.clear();
QTextCharFormat headers; headers.setForeground(QBrush(Qt::darkBlue)); QTextCharFormat headers; headers.setForeground(QBrush(Qt::darkBlue));
headers.setBackground(QBrush(QColor(230,230,240))); headers.setBackground(QBrush(QColor(230,230,240)));
@ -100,8 +178,6 @@ void HGMarkdownHighlighter::setDefaultStyles()
QTextCharFormat blockquote; blockquote.setForeground(QBrush(Qt::darkRed)); QTextCharFormat blockquote; blockquote.setForeground(QBrush(Qt::darkRed));
STY(pmh_BLOCKQUOTE, blockquote); STY(pmh_BLOCKQUOTE, blockquote);
this->setStyles(*styles);
} }
void HGMarkdownHighlighter::clearFormatting() void HGMarkdownHighlighter::clearFormatting()
@ -116,18 +192,23 @@ void HGMarkdownHighlighter::clearFormatting()
void HGMarkdownHighlighter::highlight() void HGMarkdownHighlighter::highlight()
{ {
if (cached_elements == NULL) { if (cached_elements == NULL) {
qDebug() << "cached_elements is NULL";
return; return;
} }
if (highlightingStyles == NULL) if (highlightingStyles.isEmpty())
this->setDefaultStyles(); this->setDefaultStyles();
this->clearFormatting(); this->clearFormatting();
for (int i = 0; i < highlightingStyles->size(); i++) // 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++)
{ {
HighlightingStyle style = highlightingStyles->at(i); const HighlightingStyle &style = highlightingStyles[i];
pmh_element *elem_cursor = cached_elements[style.type]; pmh_element *elem_cursor = cached_elements[style.type];
while (elem_cursor != NULL) while (elem_cursor != NULL)
{ {
@ -176,26 +257,26 @@ void HGMarkdownHighlighter::highlight()
} }
document->markContentsDirty(0, document->characterCount()); 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::parse() void HGMarkdownHighlighter::parse()
{ {
if (workerThread != NULL && workerThread->isRunning()) { if (workerThread->isRunning()) {
parsePending = true; parsePending = true;
return; return;
} }
QString content = document->toPlainText(); QString content = document->toPlainText();
QByteArray ba = content.toUtf8(); QByteArray ba = content.toUtf8();
char *content_cstring = strdup((char *)ba.data());
if (workerThread != NULL)
delete workerThread;
workerThread = new WorkerThread();
workerThread->content = content_cstring;
connect(workerThread, SIGNAL(finished()), this, SLOT(threadFinished()));
parsePending = false; parsePending = false;
workerThread->start(); workerThread->prepareAndStart((const char *)ba.data());
} }
void HGMarkdownHighlighter::threadFinished() void HGMarkdownHighlighter::threadFinished()
@ -205,11 +286,10 @@ void HGMarkdownHighlighter::threadFinished()
return; return;
} }
if (cached_elements != NULL) if (cached_elements != NULL) {
pmh_free_elements(cached_elements); pmh_free_elements(cached_elements);
cached_elements = workerThread->result; }
workerThread->result = NULL; cached_elements = workerThread->retriveResult();
this->highlight(); this->highlight();
} }

View File

@ -24,10 +24,21 @@ QT_END_NAMESPACE
class WorkerThread : public QThread class WorkerThread : public QThread
{ {
public: public:
WorkerThread();
~WorkerThread(); ~WorkerThread();
void prepareAndStart(const char *data);
pmh_element** retriveResult();
protected:
void run(); void run();
private:
void resizeBuffer(int newCap);
char *content; char *content;
int capacity;
pmh_element **result; pmh_element **result;
static const int initCapacity;
}; };
struct HighlightingStyle struct HighlightingStyle
@ -42,7 +53,8 @@ class HGMarkdownHighlighter : public QObject
public: public:
HGMarkdownHighlighter(QTextDocument *parent = 0, int aWaitInterval = 2000); HGMarkdownHighlighter(QTextDocument *parent = 0, int aWaitInterval = 2000);
void setStyles(QVector<HighlightingStyle> &styles); ~HGMarkdownHighlighter();
void setStyles(const QVector<HighlightingStyle> &styles);
int waitInterval; int waitInterval;
private slots: private slots:
@ -56,7 +68,7 @@ private:
WorkerThread *workerThread; WorkerThread *workerThread;
bool parsePending; bool parsePending;
pmh_element **cached_elements; pmh_element **cached_elements;
QVector<HighlightingStyle> *highlightingStyles; QVector<HighlightingStyle> highlightingStyles;
void clearFormatting(); void clearFormatting();
void highlight(); void highlight();

View File

@ -28,8 +28,10 @@ VEditor::VEditor(const QString &path, const QString &name, bool modifiable,
VEditor::~VEditor() VEditor::~VEditor()
{ {
if (noteFile) {
delete noteFile; delete noteFile;
} }
}
void VEditor::setupUI() void VEditor::setupUI()
{ {