From a983026552d3657e2113b443ecf2774ae21b31c9 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Sun, 23 Oct 2016 10:35:31 +0800 Subject: [PATCH] add Hoedown for markdown convertion Signed-off-by: Le Tan --- .gitmodules | 3 ++ VNote.pro | 5 ++- hoedown | 1 + src/resources/post_template.html | 1 - src/resources/pre_template.html | 6 ++-- src/src.pro | 13 ++++++-- src/vconfigmanager.cpp | 5 +++ src/vconfigmanager.h | 36 ++++++++++++++++++++++ src/veditor.cpp | 31 +++++++++++++++---- src/veditor.h | 4 +++ src/vmarkdownconverter.cpp | 52 ++++++++++++++++++++++++++++++++ src/vmarkdownconverter.h | 28 +++++++++++++++++ src/vnote.cpp | 15 +++++++-- src/vnote.h | 5 +++ 14 files changed, 191 insertions(+), 14 deletions(-) create mode 160000 hoedown create mode 100644 src/vmarkdownconverter.cpp create mode 100644 src/vmarkdownconverter.h diff --git a/.gitmodules b/.gitmodules index 94cad134..970115b7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "src/utils/marked"] path = src/utils/marked url = https://github.com/chjj/marked.git +[submodule "hoedown"] + path = hoedown + url = https://github.com/tamlok/hoedown.git diff --git a/VNote.pro b/VNote.pro index 4d67b457..075b81ec 100644 --- a/VNote.pro +++ b/VNote.pro @@ -8,4 +8,7 @@ TEMPLATE = subdirs CONFIG += c++11 -SUBDIRS = src +SUBDIRS = hoedown \ + src + +src.depends = hoedown diff --git a/hoedown b/hoedown new file mode 160000 index 00000000..845cdbc3 --- /dev/null +++ b/hoedown @@ -0,0 +1 @@ +Subproject commit 845cdbc3588129e4c1ddf8397a3ab742c6e3d9c4 diff --git a/src/resources/post_template.html b/src/resources/post_template.html index 9943ff0f..308b1d01 100644 --- a/src/resources/post_template.html +++ b/src/resources/post_template.html @@ -1,3 +1,2 @@ - diff --git a/src/resources/pre_template.html b/src/resources/pre_template.html index aa0205b3..cfce4cd2 100644 --- a/src/resources/pre_template.html +++ b/src/resources/pre_template.html @@ -2,7 +2,9 @@ - + + + + -
diff --git a/src/src.pro b/src/src.pro index 8c1fe8af..c40db8fb 100644 --- a/src/src.pro +++ b/src/src.pro @@ -32,7 +32,8 @@ SOURCES += main.cpp\ hgmarkdownhighlighter.cpp \ vstyleparser.cpp \ utils/peg-highlight/pmh_styleparser.c \ - dialog/vnewnotebookdialog.cpp + dialog/vnewnotebookdialog.cpp \ + vmarkdownconverter.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -55,7 +56,15 @@ HEADERS += vmainwindow.h \ utils/peg-highlight/pmh_definitions.h \ vstyleparser.h \ utils/peg-highlight/pmh_styleparser.h \ - dialog/vnewnotebookdialog.h + dialog/vnewnotebookdialog.h \ + vmarkdownconverter.h RESOURCES += \ vnote.qrc + +win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../hoedown/release/ -lhoedown +else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../hoedown/debug/ -lhoedown +else:unix: LIBS += -L$$OUT_PWD/../hoedown/ -lhoedown + +INCLUDEPATH += $$PWD/../hoedown +DEPENDPATH += $$PWD/../hoedown diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index fc6dcb2b..9870d5bf 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -45,6 +45,11 @@ void VConfigManager::initialize() templateCssUrl = getConfigFromSettings("global", "template_css_url").toString(); curNotebookIndex = getConfigFromSettings("global", "current_notebook").toInt(); + markdownExtensions = hoedown_extensions(HOEDOWN_EXT_TABLES | HOEDOWN_EXT_FENCED_CODE | + HOEDOWN_EXT_HIGHLIGHT | HOEDOWN_EXT_AUTOLINK | + HOEDOWN_EXT_QUOTE | HOEDOWN_EXT_MATH); + mdConverterType = (MarkdownConverterType)getConfigFromSettings("global", "markdown_converter").toInt(); + // Update notebooks readNotebookFromSettings(); diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index 1a0b4adb..81298459 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -8,10 +8,16 @@ #include "vnotebook.h" #include "hgmarkdownhighlighter.h" +#include "vmarkdownconverter.h" class QJsonObject; class QString; +enum MarkdownConverterType { + Hoedown = 0, + Marked +}; + class VConfigManager { public: @@ -49,6 +55,12 @@ public: inline const QVector& getNotebooks() const; inline void setNotebooks(const QVector ¬ebooks); + inline hoedown_extensions getMarkdownExtensions() const; + inline MarkdownConverterType getMdConverterType() const; + + inline QString getPreTemplatePath() const; + inline QString getPostTemplatePath() const; + private: void updateMarkdownEditStyle(); QVariant getConfigFromSettings(const QString §ion, const QString &key); @@ -69,6 +81,10 @@ private: int curNotebookIndex; QVector notebooks; + // Markdown Converter + hoedown_extensions markdownExtensions; + MarkdownConverterType mdConverterType; + // The name of the config file in each directory static const QString dirConfigFileName; // The name of the default configuration file @@ -140,4 +156,24 @@ inline void VConfigManager::setNotebooks(const QVector ¬ebooks) writeNotebookToSettings(); } +inline hoedown_extensions VConfigManager::getMarkdownExtensions() const +{ + return markdownExtensions; +} + +inline MarkdownConverterType VConfigManager::getMdConverterType() const +{ + return mdConverterType; +} + +inline QString VConfigManager::getPreTemplatePath() const +{ + return preTemplatePath; +} + +inline QString VConfigManager::getPostTemplatePath() const +{ + return postTemplatePath; +} + #endif // VCONFIGMANAGER_H diff --git a/src/veditor.cpp b/src/veditor.cpp index c3e7f8ff..8454a642 100644 --- a/src/veditor.cpp +++ b/src/veditor.cpp @@ -10,12 +10,13 @@ #include "vpreviewpage.h" #include "hgmarkdownhighlighter.h" #include "vconfigmanager.h" +#include "vmarkdownconverter.h" extern VConfigManager vconfig; VEditor::VEditor(const QString &path, const QString &name, bool modifiable, QWidget *parent) - : QStackedWidget(parent) + : QStackedWidget(parent), mdConverterType(vconfig.getMdConverterType()) { DocType docType = isMarkdown(name) ? DocType::Markdown : DocType::Html; QString fileText = VUtils::readFileFromDisk(QDir(path).filePath(name)); @@ -83,7 +84,11 @@ void VEditor::showFileReadMode() setCurrentWidget(textBrowser); break; case DocType::Markdown: - document.setText(noteFile->content); + if (mdConverterType == MarkdownConverterType::Marked) { + document.setText(noteFile->content); + } else { + previewByConverter(); + } setCurrentWidget(webPreviewer); break; default: @@ -91,6 +96,18 @@ void VEditor::showFileReadMode() } } +void VEditor::previewByConverter() +{ + VMarkdownConverter mdConverter; + QString content = noteFile->content; + QString html = mdConverter.generateHtml(content, vconfig.getMarkdownExtensions()); + QRegularExpression tocExp("

\\[TOC\\]<\\/p>", QRegularExpression::CaseInsensitiveOption); + QString toc = mdConverter.generateToc(content, vconfig.getMarkdownExtensions()); + html.replace(tocExp, toc); + QString completeHtml = VNote::preTemplateHtml + html + VNote::postTemplateHtml; + webPreviewer->setHtml(completeHtml, QUrl::fromLocalFile(noteFile->path + QDir::separator())); +} + void VEditor::showFileEditMode() { isEditMode = true; @@ -175,10 +192,12 @@ void VEditor::setupMarkdownPreview() VPreviewPage *page = new VPreviewPage(this); webPreviewer->setPage(page); - QWebChannel *channel = new QWebChannel(this); - channel->registerObject(QStringLiteral("content"), &document); - page->setWebChannel(channel); - webPreviewer->setHtml(VNote::templateHtml, QUrl::fromLocalFile(noteFile->path + QDir::separator())); + if (mdConverterType == MarkdownConverterType::Marked) { + QWebChannel *channel = new QWebChannel(this); + channel->registerObject(QStringLiteral("content"), &document); + page->setWebChannel(channel); + webPreviewer->setHtml(VNote::templateHtml, QUrl::fromLocalFile(noteFile->path + QDir::separator())); + } addWidget(webPreviewer); } diff --git a/src/veditor.h b/src/veditor.h index 4f5b3230..50c39a71 100644 --- a/src/veditor.h +++ b/src/veditor.h @@ -6,6 +6,8 @@ #include "vconstants.h" #include "vnotefile.h" #include "vdocument.h" +#include "vmarkdownconverter.h" +#include "vconfigmanager.h" class QTextBrowser; class VEdit; @@ -31,6 +33,7 @@ private: void showFileReadMode(); void showFileEditMode(); void setupMarkdownPreview(); + void previewByConverter(); VNoteFile *noteFile; bool isEditMode; @@ -38,6 +41,7 @@ private: VEdit *textEditor; QWebEngineView *webPreviewer; VDocument document; + MarkdownConverterType mdConverterType; }; #endif // VEDITOR_H diff --git a/src/vmarkdownconverter.cpp b/src/vmarkdownconverter.cpp new file mode 100644 index 00000000..b65579a6 --- /dev/null +++ b/src/vmarkdownconverter.cpp @@ -0,0 +1,52 @@ +#include "vmarkdownconverter.h" + +VMarkdownConverter::VMarkdownConverter() +{ + hoedownHtmlFlags = (hoedown_html_flags)0; + nestingLevel = 16; + + htmlRenderer = hoedown_html_renderer_new(hoedownHtmlFlags, nestingLevel); + tocRenderer = hoedown_html_toc_renderer_new(nestingLevel); +} + +VMarkdownConverter::~VMarkdownConverter() +{ + if (htmlRenderer) { + hoedown_html_renderer_free(htmlRenderer); + } + if (tocRenderer) { + hoedown_html_renderer_free(tocRenderer); + } +} + +QString VMarkdownConverter::generateHtml(const QString &markdown, hoedown_extensions options) +{ + if (markdown.isEmpty()) { + return QString(); + } + hoedown_document *document = hoedown_document_new(htmlRenderer, options, + nestingLevel); + QByteArray data = markdown.toUtf8(); + hoedown_buffer *outBuf = hoedown_buffer_new(data.size()); + hoedown_document_render(document, outBuf, (const uint8_t *)data.constData(), data.size()); + hoedown_document_free(document); + QString html = QString::fromUtf8(hoedown_buffer_cstr(outBuf)); + hoedown_buffer_free(outBuf); + + return html; +} + +QString VMarkdownConverter::generateToc(const QString &markdown, hoedown_extensions options) +{ + if (markdown.isEmpty()) { + return QString(); + } + hoedown_document *document = hoedown_document_new(tocRenderer, options, nestingLevel); + QByteArray data = markdown.toUtf8(); + hoedown_buffer *outBuf = hoedown_buffer_new(16); + hoedown_document_render(document, outBuf, (const uint8_t *)data.constData(), data.size()); + hoedown_document_free(document); + QString toc = QString::fromUtf8(hoedown_buffer_cstr(outBuf)); + hoedown_buffer_free(outBuf); + return toc; +} diff --git a/src/vmarkdownconverter.h b/src/vmarkdownconverter.h new file mode 100644 index 00000000..84fe4bf3 --- /dev/null +++ b/src/vmarkdownconverter.h @@ -0,0 +1,28 @@ +#ifndef VMARKDOWNCONVERTER_H +#define VMARKDOWNCONVERTER_H + +#include + +extern "C" { +#include +#include +} + +class VMarkdownConverter +{ +public: + VMarkdownConverter(); + ~VMarkdownConverter(); + + QString generateHtml(const QString &markdown, hoedown_extensions options); + QString generateToc(const QString &markdown, hoedown_extensions options); + +private: + // VMarkdownDocument *generateDocument(const QString &markdown); + hoedown_html_flags hoedownHtmlFlags; + int nestingLevel; + hoedown_renderer *htmlRenderer; + hoedown_renderer *tocRenderer; +}; + +#endif // VMARKDOWNCONVERTER_H diff --git a/src/vnote.cpp b/src/vnote.cpp index f77b2690..302b4c49 100644 --- a/src/vnote.cpp +++ b/src/vnote.cpp @@ -10,6 +10,8 @@ VConfigManager vconfig; QString VNote::templateHtml; +QString VNote::preTemplateHtml; +QString VNote::postTemplateHtml; VNote::VNote() : QObject() { @@ -21,8 +23,17 @@ VNote::VNote() : QObject() void VNote::decorateTemplate() { - templateHtml = VUtils::readFileFromDisk(vconfig.getTemplatePath()); - templateHtml.replace("CSS_PLACE_HOLDER", vconfig.getTemplateCssUrl()); + if (templateHtml.isEmpty()) { + templateHtml = VUtils::readFileFromDisk(vconfig.getTemplatePath()); + templateHtml.replace("CSS_PLACE_HOLDER", vconfig.getTemplateCssUrl()); + } + if (preTemplateHtml.isEmpty()) { + preTemplateHtml = VUtils::readFileFromDisk(vconfig.getPreTemplatePath()); + preTemplateHtml.replace("CSS_PLACE_HOLDER", vconfig.getTemplateCssUrl()); + } + if (postTemplateHtml.isEmpty()) { + postTemplateHtml = VUtils::readFileFromDisk(vconfig.getPostTemplatePath()); + } } const QVector& VNote::getNotebooks() diff --git a/src/vnote.h b/src/vnote.h index f841ed4c..fa92a64e 100644 --- a/src/vnote.h +++ b/src/vnote.h @@ -20,8 +20,13 @@ public: static void decorateTemplate(); + // Used by Marked static QString templateHtml; + // Used by other markdown converter + static QString preTemplateHtml; + static QString postTemplateHtml; + void createNotebook(const QString &name, const QString &path); void removeNotebook(const QString &name);