Browse Source

markdown-it: aware of YAML format metadata in notes

Le Tan 7 years ago
parent
commit
4afefbe964

+ 1 - 0
README.md

@@ -190,6 +190,7 @@ In VNote, almost everything is configurable, such as background color, font, and
 - [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) (MIT License)
 - [markdown-it-sub](https://github.com/markdown-it/markdown-it-sub) (MIT License)
 - [markdown-it-sup](https://github.com/markdown-it/markdown-it-sup) (MIT License)
+- [markdown-it-front-matter](https://github.com/craigdmckenna/markdown-it-front-matter) (MIT License)
 - [mermaid 7.0.0](https://github.com/knsv/mermaid) (MIT License)
 - [MathJax](https://www.mathjax.org/) (Apache-2.0)
 - [showdown](https://github.com/showdownjs/showdown) (Unknown)

+ 1 - 0
README_zh.md

@@ -193,6 +193,7 @@ VNote中,几乎一切都是可以定制的,例如背景颜色、字体以及
 - [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) (MIT License)
 - [markdown-it-sub](https://github.com/markdown-it/markdown-it-sub) (MIT License)
 - [markdown-it-sup](https://github.com/markdown-it/markdown-it-sup) (MIT License)
+- [markdown-it-front-matter](https://github.com/craigdmckenna/markdown-it-front-matter) (MIT License)
 - [mermaid 7.0.0](https://github.com/knsv/mermaid) (MIT License)
 - [MathJax](https://www.mathjax.org/) (Apache-2.0)
 - [showdown](https://github.com/showdownjs/showdown) (Unknown)

+ 108 - 0
src/markdownitoption.h

@@ -0,0 +1,108 @@
+#ifndef MARKDOWNITOPTION_H
+#define MARKDOWNITOPTION_H
+
+#include <QStringList>
+
+struct MarkdownitOption
+{
+    MarkdownitOption()
+        : MarkdownitOption(true,
+                           false,
+                           true,
+                           false,
+                           false,
+                           false)
+    {
+    }
+
+    MarkdownitOption(bool p_html,
+                     bool p_breaks,
+                     bool p_linkify,
+                     bool p_sub,
+                     bool p_sup,
+                     bool p_metadata)
+        : m_html(p_html),
+          m_breaks(p_breaks),
+          m_linkify(p_linkify),
+          m_sub(p_sub),
+          m_sup(p_sup),
+          m_metadata(p_metadata)
+    {
+    }
+
+    QStringList toConfig() const
+    {
+        QStringList conf;
+        if (m_html) {
+            conf << "html";
+        }
+
+        if (m_breaks) {
+            conf << "break";
+        }
+
+        if (m_linkify) {
+            conf << "linkify";
+        }
+
+        if (m_sub) {
+            conf << "sub";
+        }
+
+        if (m_sup) {
+            conf << "sup";
+        }
+
+        if (m_metadata) {
+            conf << "metadata";
+        }
+
+        return conf;
+    }
+
+    static MarkdownitOption fromConfig(const QStringList &p_conf)
+    {
+        return MarkdownitOption(testOption(p_conf, "html"),
+                                testOption(p_conf, "break"),
+                                testOption(p_conf, "linkify"),
+                                testOption(p_conf, "sub"),
+                                testOption(p_conf, "sup"),
+                                testOption(p_conf, "metadata"));
+    }
+
+    bool operator==(const MarkdownitOption &p_opt) const
+    {
+        return m_html == p_opt.m_html
+               && m_breaks == p_opt.m_breaks
+               && m_linkify == p_opt.m_linkify
+               && m_sub == p_opt.m_sub
+               && m_sup == p_opt.m_sup
+               && m_metadata == p_opt.m_metadata;
+    }
+
+    // Eanble HTML tags in source.
+    bool m_html;
+
+    // Convert '\n' in paragraphs into <br>.
+    bool m_breaks;
+
+    // Auto-convert URL-like text to links.
+    bool m_linkify;
+
+    // Enable subscript.
+    bool m_sub;
+
+    // Enable superscript.
+    bool m_sup;
+
+    // Enable metadata in YML format.
+    bool m_metadata;
+
+private:
+    static bool testOption(const QStringList &p_conf, const QString &p_opt)
+    {
+        return p_conf.contains(p_opt);
+    }
+};
+
+#endif // MARKDOWNITOPTION_H

+ 4 - 0
src/resources/markdown-it.js

@@ -82,6 +82,10 @@ if (VMarkdownitOption.sup) {
     mdit = mdit.use(window.markdownitSup);
 }
 
+if (VMarkdownitOption.metadata) {
+    mdit = mdit.use(window.markdownitFrontMatter, function(text){});
+}
+
 mdit = mdit.use(window.markdownitFootnote);
 
 var mdHasTocSection = function(markdown) {

+ 2 - 1
src/resources/vnote.ini

@@ -234,7 +234,8 @@ custom_export=
 ; linkify: auto-convert URL-like text to links
 ; sub: subscript;
 ; sup: superscript;
-markdownit_opt=html,linkify
+; metadata: metadata aware;
+markdownit_opt=html,linkify,metadata
 
 ; Location and configuration for Mathjax
 mathjax_javascript=https://cdn.bootcss.com/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_HTMLorMML

+ 2 - 1
src/src.pro

@@ -253,7 +253,8 @@ HEADERS  += vmainwindow.h \
     vlivepreviewhelper.h \
     vmathjaxpreviewhelper.h \
     vmathjaxwebdocument.h \
-    vmathjaxinplacepreviewhelper.h
+    vmathjaxinplacepreviewhelper.h \
+    markdownitoption.h
 
 RESOURCES += \
     vnote.qrc \

+ 4 - 0
src/utils/markdown-it/README.md

@@ -23,3 +23,7 @@ Vitaly Puzrin
 # [markdown-it-sup](https://github.com/markdown-it/markdown-it-sup)
 v1.0.0
 Vitaly Puzrin
+
+# [markddown-it-front-matter](https://github.com/craigdmckenna/markdown-it-front-matter)
+v0.1.2
+Craig McKenna

+ 120 - 0
src/utils/markdown-it/markdown-it-front-matter.js

@@ -0,0 +1,120 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.markdownitFrontMatter = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+
+// Process front matter and pass to cb
+//
+'use strict';
+
+module.exports = function front_matter_plugin(md, cb) {
+  var min_markers = 3,
+      marker_str  = '-',
+      marker_char = marker_str.charCodeAt(0),
+      marker_len  = marker_str.length
+
+  function frontMatter(state, startLine, endLine, silent) {
+    var pos, nextLine, marker_count, markup, token,
+        old_parent, old_line_max, start_content,
+        auto_closed = false,
+        start = state.bMarks[startLine] + state.tShift[startLine],
+        max = state.eMarks[startLine];
+
+    // Check out the first character of the first line quickly,
+    // this should filter out non-front matter
+    //
+    if (startLine !== 0 || marker_char !== state.src.charCodeAt(0)) { return false; }
+
+    // Check out the rest of the marker string
+    //
+    for (pos = start + 1; pos <= max; pos++) { // while pos <= 3
+      if (marker_str[(pos - start) % marker_len] !== state.src[pos]) {
+        start_content = pos + 1
+        break;
+      }
+    }
+
+    marker_count = Math.floor((pos - start) / marker_len);
+
+    if (marker_count < min_markers) { return false; }
+    pos -= (pos - start) % marker_len;
+
+    // Since start is found, we can report success here in validation mode
+    //
+    if (silent) { return true; }
+
+    // Search for the end of the block
+    //
+    nextLine = startLine;
+
+    for (;;) {
+      nextLine++;
+      if (nextLine >= endLine) {
+        // unclosed block should be autoclosed by end of document.
+        // also block seems to be autoclosed by end of parent
+        break;
+      }
+
+      start = state.bMarks[nextLine] + state.tShift[nextLine];
+      max = state.eMarks[nextLine];
+
+      if (start < max && state.sCount[nextLine] < state.blkIndent) {
+        // non-empty line with negative indent should stop the list:
+        // - ```
+        //  test
+        break;
+      }
+
+      if (marker_char !== state.src.charCodeAt(start)) { continue; }
+
+      if (state.sCount[nextLine] - state.blkIndent >= 4) {
+        // closing fence should be indented less than 4 spaces
+        continue;
+      }
+
+      for (pos = start + 1; pos <= max; pos++) {
+        if (marker_str[(pos - start) % marker_len] !== state.src[pos]) {
+          break;
+        }
+      }
+
+      // closing code fence must be at least as long as the opening one
+      if (Math.floor((pos - start) / marker_len) < marker_count) { continue; }
+
+      // make sure tail has spaces only
+      pos -= (pos - start) % marker_len;
+      pos = state.skipSpaces(pos);
+
+      if (pos < max) { continue; }
+
+      // found!
+      auto_closed = true;
+      break;
+    }
+
+    old_parent = state.parentType;
+    old_line_max = state.lineMax;
+    state.parentType = 'container';
+
+    // this will prevent lazy continuations from ever going past our end marker
+    state.lineMax = nextLine;
+
+    token        = state.push('front_matter', null, 0);
+    token.hidden = true;
+    token.markup = state.src.slice(startLine, pos)
+    token.block  = true;
+    token.map    = [ startLine, pos ];
+
+    state.parentType = old_parent;
+    state.lineMax = old_line_max;
+    state.line = nextLine + (auto_closed ? 1 : 0);
+
+    cb(state.src.slice(start_content, start - 1))
+
+    return true;
+  }
+
+  md.block.ruler.before('table', 'front_matter', frontMatter, {
+    alt: [ 'paragraph', 'reference', 'blockquote', 'list' ]
+  });
+};
+
+},{}]},{},[1])(1)
+});

+ 8 - 2
src/utils/vutils.cpp

@@ -665,18 +665,24 @@ QString VUtils::generateHtmlTemplate(const QString &p_template,
             extraFile += "<script src=\"qrc" + VNote::c_markdownitSubExtraFile + "\"></script>\n";
         }
 
+        if (opt.m_metadata) {
+            extraFile += "<script src=\"qrc" + VNote::c_markdownitFrontMatterExtraFile + "\"></script>\n";
+        }
+
         QString optJs = QString("<script>var VMarkdownitOption = {"
                                 "html: %1,\n"
                                 "breaks: %2,\n"
                                 "linkify: %3,\n"
                                 "sub: %4,\n"
-                                "sup: %5 };\n"
+                                "sup: %5,\n"
+                                "metadata: %6 };\n"
                                 "</script>\n")
                                .arg(opt.m_html ? QStringLiteral("true") : QStringLiteral("false"))
                                .arg(opt.m_breaks ? QStringLiteral("true") : QStringLiteral("false"))
                                .arg(opt.m_linkify ? QStringLiteral("true") : QStringLiteral("false"))
                                .arg(opt.m_sub ? QStringLiteral("true") : QStringLiteral("false"))
-                               .arg(opt.m_sup ? QStringLiteral("true") : QStringLiteral("false"));
+                               .arg(opt.m_sup ? QStringLiteral("true") : QStringLiteral("false"))
+                               .arg(opt.m_metadata ? QStringLiteral("true") : QStringLiteral("false"));
         extraFile += optJs;
         break;
     }

+ 2 - 0
src/vconfigmanager.h

@@ -7,12 +7,14 @@
 #include <QVector>
 #include <QSettings>
 #include <QHash>
+
 #include "vnotebook.h"
 #include "hgmarkdownhighlighter.h"
 #include "vmarkdownconverter.h"
 #include "vconstants.h"
 #include "vfilesessioninfo.h"
 #include "utils/vmetawordmanager.h"
+#include "markdownitoption.h"
 
 class QJsonObject;
 class QString;

+ 0 - 91
src/vconstants.h

@@ -173,95 +173,4 @@ enum PlantUMLMode
     OnlinePlantUML = 1,
     LocalPlantUML = 2
 };
-
-
-struct MarkdownitOption
-{
-    MarkdownitOption()
-        : MarkdownitOption(true,
-                           false,
-                           true,
-                           false,
-                           false)
-    {
-    }
-
-    MarkdownitOption(bool p_html,
-                     bool p_breaks,
-                     bool p_linkify,
-                     bool p_sub,
-                     bool p_sup)
-        : m_html(p_html),
-          m_breaks(p_breaks),
-          m_linkify(p_linkify),
-          m_sub(p_sub),
-          m_sup(p_sup)
-    {
-    }
-
-    QStringList toConfig() const
-    {
-        QStringList conf;
-        if (m_html) {
-            conf << "html";
-        }
-
-        if (m_breaks) {
-            conf << "break";
-        }
-
-        if (m_linkify) {
-            conf << "linkify";
-        }
-
-        if (m_sub) {
-            conf << "sub";
-        }
-
-        if (m_sup) {
-            conf << "sup";
-        }
-
-        return conf;
-    }
-
-    static MarkdownitOption fromConfig(const QStringList &p_conf)
-    {
-        return MarkdownitOption(testOption(p_conf, "html"),
-                                testOption(p_conf, "break"),
-                                testOption(p_conf, "linkify"),
-                                testOption(p_conf, "sub"),
-                                testOption(p_conf, "sup"));
-    }
-
-    bool operator==(const MarkdownitOption &p_opt) const
-    {
-        return m_html == p_opt.m_html
-               && m_breaks == p_opt.m_breaks
-               && m_linkify == p_opt.m_linkify
-               && m_sub == p_opt.m_sub
-               && m_sup == p_opt.m_sup;
-    }
-
-    // Eanble HTML tags in source.
-    bool m_html;
-
-    // Convert '\n' in paragraphs into <br>.
-    bool m_breaks;
-
-    // Auto-convert URL-like text to links.
-    bool m_linkify;
-
-    // Enable subscript.
-    bool m_sub;
-
-    // Enable superscript.
-    bool m_sup;
-
-private:
-    static bool testOption(const QStringList &p_conf, const QString &p_opt)
-    {
-        return p_conf.contains(p_opt);
-    }
-};
 #endif

+ 12 - 0
src/vmainwindow.cpp

@@ -1600,11 +1600,23 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu)
                 g_config->setMarkdownitOption(opt);
             });
 
+    QAction *metadataAct = new QAction(tr("Metadata Aware"), this);
+    metadataAct->setToolTip(tr("Be aware of metadata in YAML format"));
+    metadataAct->setCheckable(true);
+    metadataAct->setChecked(opt.m_metadata);
+    connect(metadataAct, &QAction::triggered,
+            this, [this](bool p_checked) {
+                MarkdownitOption opt = g_config->getMarkdownitOption();
+                opt.m_metadata = p_checked;
+                g_config->setMarkdownitOption(opt);
+            });
+
     optMenu->addAction(htmlAct);
     optMenu->addAction(breaksAct);
     optMenu->addAction(linkifyAct);
     optMenu->addAction(supAct);
     optMenu->addAction(subAct);
+    optMenu->addAction(metadataAct);
 }
 
 void VMainWindow::initRenderBackgroundMenu(QMenu *menu)

+ 1 - 0
src/vnote.cpp

@@ -39,6 +39,7 @@ const QString VNote::c_markdownitTaskListExtraFile = ":/utils/markdown-it/markdo
 const QString VNote::c_markdownitSubExtraFile = ":/utils/markdown-it/markdown-it-sub.min.js";
 const QString VNote::c_markdownitSupExtraFile = ":/utils/markdown-it/markdown-it-sup.min.js";
 const QString VNote::c_markdownitFootnoteExtraFile = ":/utils/markdown-it/markdown-it-footnote.min.js";
+const QString VNote::c_markdownitFrontMatterExtraFile = ":/utils/markdown-it/markdown-it-front-matter.js";
 
 const QString VNote::c_showdownJsFile = ":/resources/showdown.js";
 const QString VNote::c_showdownExtraFile = ":/utils/showdown/showdown.min.js";

+ 1 - 0
src/vnote.h

@@ -50,6 +50,7 @@ public:
     static const QString c_markdownitSubExtraFile;
     static const QString c_markdownitSupExtraFile;
     static const QString c_markdownitFootnoteExtraFile;
+    static const QString c_markdownitFrontMatterExtraFile;
 
     // Showdown
     static const QString c_showdownJsFile;

+ 1 - 0
src/vnote.qrc

@@ -213,5 +213,6 @@
         <file>resources/mathjax_preview.js</file>
         <file>resources/mathjax_preview_template.html</file>
         <file>utils/dom-to-image/dom-to-image.js</file>
+        <file>utils/markdown-it/markdown-it-front-matter.js</file>
     </qresource>
 </RCC>