Browse Source

markdown: support WaveDrom diagram in language `wavedrom` code block

In place preview is not supported.
Le Tan 7 years ago
parent
commit
12d5bb27ef

+ 2 - 3
src/peghighlighterresult.cpp

@@ -52,7 +52,7 @@ PegHighlighterResult::PegHighlighterResult(const PegMarkdownHighlighter *p_peg,
 
     parseHRuleBlocks(p_peg, p_result);
 
-    parseTableBlocks(p_peg, p_result);
+    parseTableBlocks(p_result);
 }
 
 static bool compHLUnit(const HLUnit &p_a, const HLUnit &p_b)
@@ -272,8 +272,7 @@ void PegHighlighterResult::parseFencedCodeBlocks(const PegMarkdownHighlighter *p
     }
 }
 
-void PegHighlighterResult::parseTableBlocks(const PegMarkdownHighlighter *p_peg,
-                                            const QSharedPointer<PegParseResult> &p_result)
+void PegHighlighterResult::parseTableBlocks(const QSharedPointer<PegParseResult> &p_result)
 {
     const QVector<VElementRegion> &tableRegs = p_result->m_tableRegions;
     const QVector<VElementRegion> &headerRegs = p_result->m_tableHeaderRegions;

+ 1 - 2
src/peghighlighterresult.h

@@ -113,8 +113,7 @@ private:
                           const QSharedPointer<PegParseResult> &p_result);
 
     // Parse table blocks from parse results.
-    void parseTableBlocks(const PegMarkdownHighlighter *p_peg,
-                          const QSharedPointer<PegParseResult> &p_result);
+    void parseTableBlocks(const QSharedPointer<PegParseResult> &p_result);
 
 #if 0
     void parseBlocksElementRegionOne(QHash<int, QVector<VElementRegion>> &p_regs,

+ 8 - 2
src/resources/docs/markdown_guide_en.md

@@ -122,9 +122,15 @@ As VNote suggests:
 
 ### Diagrams
 
-> You need to enable Flowchart.js or Mermaid in the `Markdown` menu.
+> You need to enable Flowchart.js or Mermaid or WaveDrom in the `Markdown` menu and restart current opened tabs.
 
-VNote supports [Flowchart.js](http://flowchart.js.org/) and [Mermaid](https://mermaidjs.github.io/) to draw diagrams such as *flowchart* and *sequence diagram*. You should use `flow` or `flowchart` and `mermaid` specified as the language of the fenced code block and write the definition of your diagram within it.
+VNote supports the following engines to draw diagrams. You should specify particular language of the fenced code block and write the definition of your diagram within it.
+
+- [Flowchart.js](http://flowchart.js.org/) for *flowchart* with language `flow` or `flowchart`;
+- [Mermaid](https://mermaidjs.github.io/) with language `mermaid`;
+- [WaveDrom](https://wavedrom.com/) for *digital timing diagram* with language `wavedrom`;
+
+For example,
 
     ```flowchart
     st=>start: Start:>http://www.google.com[blank]

+ 8 - 2
src/resources/docs/markdown_guide_zh.md

@@ -123,9 +123,15 @@ As VNote suggests:
 
 ### 图表
 
-> 需要在`Markdown`菜单中启用Flowchart.js或Mermaid。
+> 需要在`Markdown`菜单中启用Flowchart.js或Mermaid或WaveDrom,并重新打开当前标签页
 
-VNote支持 [Flowchart.js](http://flowchart.js.org/) 和 [Mermaid](https://mermaidjs.github.io/) 来实现诸如*流程图*和*序列图*等图表。您需要使用代码块,并标明语言为`flow`或`flowchart`或`mermaid`,然后在代码块里面定义图表。
+VNote支持使用以下引擎来绘制图表。您需要使用代码块,并标明特定语言,然后在代码块里面定义图表。
+
+- [Flowchart.js](http://flowchart.js.org/),语言为`flow`或`flowchart`;
+- [Mermaid](https://mermaidjs.github.io/),语言为`mermaid`;
+- [WaveDrom](https://wavedrom.com/),数字时序图,语言为`wavedrom`;
+
+例如,
 
     ```flowchart
     st=>start: Start:>http://www.google.com[blank]

+ 13 - 0
src/resources/hoedown.js

@@ -2,6 +2,10 @@
 marked.setOptions({
     highlight: function(code, lang) {
         if (lang) {
+            if (lang === 'wavedrom') {
+                lang = 'json';
+            }
+
             if (hljs.getLanguage(lang)) {
                 return hljs.highlight(lang, code, true).value;
             } else {
@@ -26,6 +30,8 @@ var updateHtml = function(html) {
 
     var codes = document.getElementsByTagName('code');
     mermaidIdx = 0;
+    flowchartIdx = 0;
+    wavedromIdx = 0;
     plantUMLIdx = 0;
     graphvizIdx = 0;
     for (var i = 0; i < codes.length; ++i) {
@@ -47,6 +53,13 @@ var updateHtml = function(html) {
                     --i;
                     continue;
                 }
+            } else if (VEnableWavedrom && code.classList.contains('language-wavedrom')) {
+                // Wavedrom code block.
+                if (renderWavedromOne(code)) {
+                    // replaceChild() will decrease codes.length.
+                    --i;
+                    continue;
+                }
             } else if (VEnableMathjax && code.classList.contains('language-mathjax')) {
                 // Mathjax code block.
                 continue;

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

@@ -44,6 +44,10 @@ var mdit = window.markdownit({
     langPrefix: 'lang-',
     highlight: function(str, lang) {
         if (lang && (!specialCodeBlock(lang) || highlightSpecialBlocks)) {
+            if (lang === 'wavedrom') {
+                lang = 'json';
+            }
+
             if (hljs.getLanguage(lang)) {
                 return hljs.highlight(lang, str, true).value;
             } else {
@@ -167,6 +171,7 @@ var updateText = function(text) {
     handleMetaData();
     renderMermaid('lang-mermaid');
     renderFlowchart(['lang-flowchart', 'lang-flow']);
+    renderWavedrom('lang-wavedrom');
     renderPlantUML('lang-puml');
     renderGraphviz('lang-dot');
     addClassToCodeBlock();

+ 62 - 2
src/resources/markdown_template.js

@@ -18,6 +18,7 @@ var pendingKeys = [];
 
 var VMermaidDivClass = 'mermaid-diagram';
 var VFlowchartDivClass = 'flowchart-diagram';
+var VWavedromDivClass = 'wavedrom-diagram';
 var VPlantUMLDivClass = 'plantuml-diagram';
 var VMetaDataCodeClass = 'markdown-metadata';
 var VMarkRectDivClass = 'mark-rect';
@@ -40,6 +41,10 @@ if (typeof VEnableMathjax == 'undefined') {
     VEnableMathjax = false;
 }
 
+if (typeof VEnableWavedrom == 'undefined') {
+    VEnableWavedrom = false;
+}
+
 if (typeof VEnableHighlightLineNumber == 'undefined') {
     VEnableHighlightLineNumber = false;
 }
@@ -707,6 +712,58 @@ var renderFlowchartOne = function(code) {
     return true;
 };
 
+var wavedromIdx = 0;
+
+var renderWavedrom = function(className) {
+    if (!VEnableWavedrom) {
+        return;
+    }
+
+    var codes = document.getElementsByTagName('code');
+    wavedromIdx = 0;
+    for (var i = 0; i < codes.length; ++i) {
+        var code = codes[i];
+        if (code.classList.contains(className)) {
+            if (renderWavedromOne(code)) {
+                // replaceChild() will decrease codes.length.
+                --i;
+            }
+        }
+    }
+};
+
+var renderWavedromOne = function(code) {
+    // Create a script element.
+    var script = document.createElement('script');
+    script.setAttribute('type', 'WaveDrom');
+    script.textContent = code.textContent;
+    script.setAttribute('id', 'WaveDrom_JSON_' + wavedromIdx);
+
+    var preNode = code.parentNode;
+    preNode.parentNode.replaceChild(script, preNode);
+
+    // Create a div element.
+    var div = document.createElement('div');
+    div.setAttribute('id', 'WaveDrom_Display_' + wavedromIdx);
+    div.classList.add(VWavedromDivClass);
+    script.insertAdjacentElement('afterend', div);
+
+    try {
+        WaveDrom.RenderWaveForm(wavedromIdx,
+                                WaveDrom.eva(script.getAttribute('id')),
+                                'WaveDrom_Display_');
+    } catch (err) {
+        wavedromIdx++;
+        content.setLog("err: " + err);
+        return false;
+    }
+
+    script.parentNode.removeChild(script);
+
+    wavedromIdx++;
+    return true;
+};
+
 var plantUMLIdx = 0;
 var plantUMLCodeClass = 'plantuml_code_';
 
@@ -1365,7 +1422,8 @@ var specialCodeBlock = function(lang) {
            || (VEnableMermaid && lang == 'mermaid')
            || (VEnableFlowchart && (lang == 'flowchart' || lang == 'flow'))
            || (VPlantUMLMode != 0 && lang == 'puml')
-           || (VEnableGraphviz && lang == 'dot');
+           || (VEnableGraphviz && lang == 'dot')
+           || (VEnableWavedrom && lang === 'wavedrom');
 };
 
 var handlePlantUMLResult = function(id, timeStamp, format, result) {
@@ -1449,6 +1507,7 @@ var previewCodeBlock = function(id, lang, text, isLivePreview) {
         || (lang != 'flow'
             && lang != 'flowchart'
             && lang != 'mermaid'
+            && lang != 'wavedrom'
             && (lang != 'puml' || VPlantUMLMode != 1 || !isLivePreview))) {
         return;
     }
@@ -1464,6 +1523,8 @@ var previewCodeBlock = function(id, lang, text, isLivePreview) {
         renderFlowchartOne(code);
     } else if (lang == 'mermaid') {
         renderMermaidOne(code);
+    } else if (lang == 'wavedrom') {
+        renderWavedromOne(code);
     } else if (lang == 'puml') {
         renderPlantUMLOneOnline(code);
     }
@@ -1873,4 +1934,3 @@ var clearMarkRectDivs = function() {
 var startFreshRender = function() {
     skipScrollCheckRange = null;
 };
-

+ 5 - 0
src/resources/marked.js

@@ -18,6 +18,10 @@ renderer.heading = function(text, level) {
 marked.setOptions({
     highlight: function(code, lang) {
         if (lang && (!specialCodeBlock(lang) || highlightSpecialBlocks)) {
+            if (lang === 'wavedrom') {
+                lang = 'json';
+            }
+
             if (hljs.getLanguage(lang)) {
                 return hljs.highlight(lang, code, true).value;
             } else {
@@ -63,6 +67,7 @@ var updateText = function(text) {
     setupImageView();
     renderMermaid('language-mermaid');
     renderFlowchart(['language-flowchart', 'language-flow']);
+    renderWavedrom('language-wavedrom');
     renderPlantUML('language-puml');
     renderGraphviz('language-dot');
     addClassToCodeBlock();

+ 37 - 0
src/resources/mathjax_preview.js

@@ -6,6 +6,7 @@ var content;
 
 var VMermaidDivClass = 'mermaid-diagram';
 var VFlowchartDivClass = 'flowchart-diagram';
+var VWavedromDivClass = 'wavedrom-diagram';
 var VPlantUMLDivClass = 'plantuml-diagram';
 
 if (typeof VPlantUMLServer == 'undefined') {
@@ -112,6 +113,8 @@ var mermaidIdx = 0;
 
 var flowchartIdx = 0;
 
+var wavedromIdx = 0;
+
 if (typeof mermaidAPI != "undefined") {
     mermaidAPI.parseError = function(err, hash) {
         mermaidParserErr = true;
@@ -135,6 +138,8 @@ var previewDiagram = function(identifier, id, timeStamp, lang, text) {
     var div = null;
     if (lang == 'flow' || lang == 'flowchart') {
         div = renderFlowchartOne(identifier, id, timeStamp, text);
+    } else if (lang === 'wavedrom') {
+        div = renderWavedromOne(identifier, id, timeStamp, text);
     } else if (lang == 'mermaid') {
         div = renderMermaidOne(identifier, id, timeStamp, text);
     } else if (lang == 'puml') {
@@ -223,6 +228,38 @@ var renderFlowchartOne = function(identifier, id, timeStamp, text) {
     return div;
 };
 
+var renderWavedromOne = function(identifier, id, timeStamp, text) {
+    // Create a script element.
+    var script = document.createElement('script');
+    script.setAttribute('type', 'WaveDrom');
+    script.textContent = text;
+    script.setAttribute('id', 'WaveDrom_JSON_' + wavedromIdx);
+
+    contentDiv.appendChild(script);
+
+    // Create a div element.
+    var div = document.createElement('div');
+    div.setAttribute('id', 'WaveDrom_Display_' + wavedromIdx);
+    div.classList.add(VWavedromDivClass);
+    script.insertAdjacentElement('afterend', div);
+
+    try {
+        WaveDrom.RenderWaveForm(wavedromIdx,
+                                WaveDrom.eva(script.getAttribute('id')),
+                                'WaveDrom_Display_');
+    } catch (err) {
+        content.setLog("err: " + err);
+        contentDiv.removeChild(script);
+        contentDiv.removeChild(div);
+        wavedromIdx++;
+        return null;
+    }
+
+    contentDiv.removeChild(script);
+    wavedromIdx++;
+    return div;
+};
+
 var renderPlantUMLOne = function(identifier, id, timeStamp, text) {
     var data = { identifier: identifier,
                  id: id,

+ 15 - 0
src/resources/showdown.js

@@ -51,6 +51,7 @@ var mdHasTocSection = function(markdown) {
 var highlightCodeBlocks = function(doc,
                                    enableMermaid,
                                    enableFlowchart,
+                                   enableWavedrom,
                                    enableMathJax,
                                    enablePlantUML,
                                    enableGraphviz) {
@@ -58,6 +59,15 @@ var highlightCodeBlocks = function(doc,
     for (var i = 0; i < codes.length; ++i) {
         var code = codes[i];
         if (code.parentElement.tagName.toLowerCase() == 'pre') {
+            if (code.classList.contains('language-wavedrom')) {
+                if (enableWavedrom) {
+                    continue;
+                } else {
+                    code.classList.remove('language-wavedrom');
+                    code.classList.add('language-json');
+                }
+            }
+
             if (enableMermaid && code.classList.contains('language-mermaid')) {
                 // Mermaid code block.
                 continue;
@@ -66,6 +76,9 @@ var highlightCodeBlocks = function(doc,
                            || code.classList.contains('language-flow'))) {
                 // Flowchart code block.
                 continue;
+            } else if (enableWavedrom && code.classList.contains('language-wavedrom')) {
+                // Wavedrom code block.
+                continue;
             } else if (enableMathJax && code.classList.contains('language-mathjax')) {
                 // MathJax code block.
                 continue;
@@ -103,11 +116,13 @@ var updateText = function(text) {
     highlightCodeBlocks(document,
                         VEnableMermaid,
                         VEnableFlowchart,
+                        VEnableWavedrom,
                         VEnableMathjax,
                         VPlantUMLMode != 0,
                         VEnableGraphviz);
     renderMermaid('language-mermaid');
     renderFlowchart(['language-flowchart', 'language-flow']);
+    renderWavedrom('language-wavedrom');
     renderPlantUML('language-puml');
     renderGraphviz('language-dot');
     addClassToCodeBlock();

+ 9 - 0
src/resources/themes/v_detorte/v_detorte.css

@@ -196,6 +196,15 @@ div.flowchart-diagram {
     color: #222222;
 }
 
+div.wavedrom-diagram {
+    padding: 0px 5px 0px 5px;
+    margin: 16px 0px 16px 0px;
+    width: fit-content;
+    overflow: hidden;
+    background: #949494;
+    color: #222222;
+}
+
 div.plantuml-diagram {
     padding: 5px 5px 0px 5px;
     margin: 16px 0px 16px 0px;

+ 9 - 0
src/resources/themes/v_moonlight/v_moonlight.css

@@ -194,6 +194,15 @@ div.flowchart-diagram {
     color: #6C6C6C;
 }
 
+div.wavedrom-diagram {
+    padding: 0px 5px 0px 5px;
+    margin: 16px 0px 16px 0px;
+    width: fit-content;
+    overflow: hidden;
+    background: #B0BEC5;
+    color: #6C6C6C;
+}
+
 div.plantuml-diagram {
     padding: 5px 5px 0px 5px;
     margin: 16px 0px 16px 0px;

+ 7 - 0
src/resources/themes/v_native/v_native.css

@@ -190,6 +190,13 @@ div.flowchart-diagram {
     overflow: hidden;
 }
 
+div.wavedrom-diagram {
+    padding: 0px 5px 0px 5px;
+    margin: 16px 0px 16px 0px;
+    width: fit-content;
+    overflow: hidden;
+}
+
 div.plantuml-diagram {
     padding: 5px 5px 0px 5px;
     margin: 16px 0px 16px 0px;

+ 7 - 0
src/resources/themes/v_pure/v_pure.css

@@ -192,6 +192,13 @@ div.flowchart-diagram {
     overflow: hidden;
 }
 
+div.wavedrom-diagram {
+    padding: 0px 5px 0px 5px;
+    margin: 16px 0px 16px 0px;
+    width: fit-content;
+    overflow: hidden;
+}
+
 div.plantuml-diagram {
     padding: 5px 5px 0px 5px;
     margin: 16px 0px 16px 0px;

+ 4 - 0
src/resources/vnote.ini

@@ -541,3 +541,7 @@ ParseAndPaste=P
 ; Shortcut could be empty
 ; Need to escape \ and ", use double quotes to quote paths/arguments with spaces
 ; SHOULD defined in user config file, not here
+
+[markdown]
+; Enable WaveDrom
+enable_wavedrom=false

+ 9 - 0
src/utils/vutils.cpp

@@ -813,6 +813,12 @@ QString VUtils::generateHtmlTemplate(const QString &p_template,
         }
     }
 
+    if (g_config->getEnableWavedrom()) {
+        extraFile += "<script src=\"qrc" + VNote::c_wavedromThemeFile + "\"></script>\n" +
+                     "<script src=\"qrc" + VNote::c_wavedromJsFile + "\"></script>\n" +
+                     "<script>var VEnableWavedrom = true;</script>\n";
+    }
+
     int plantUMLMode = g_config->getPlantUMLMode();
     if (plantUMLMode != PlantUMLMode::DisablePlantUML) {
         if (plantUMLMode == PlantUMLMode::OnlinePlantUML) {
@@ -984,6 +990,9 @@ QString VUtils::generateMathJaxPreviewTemplate()
                  "                    messageStyle: \"none\"});\n"
                  "</script>\n";
 
+    extraFile += "<script src=\"qrc" + VNote::c_wavedromThemeFile + "\"></script>\n" +
+                 "<script src=\"qrc" + VNote::c_wavedromJsFile + "\"></script>\n";
+
     // PlantUML.
     extraFile += "<script type=\"text/javascript\" src=\"" + VNote::c_plantUMLJsFile + "\"></script>\n" +
                  "<script type=\"text/javascript\" src=\"" + VNote::c_plantUMLZopfliJsFile + "\"></script>\n" +

+ 2 - 0
src/utils/wavedrom/README.md

@@ -0,0 +1,2 @@
+# [WaveDrom](https://github.com/wavedrom/wavedrom)
+v1.6.2

File diff suppressed because it is too large
+ 0 - 0
src/utils/wavedrom/wavedrom-theme.js


File diff suppressed because it is too large
+ 2 - 0
src/utils/wavedrom/wavedrom.min.js


+ 21 - 11
src/vconfigmanager.cpp

@@ -327,26 +327,30 @@ void VConfigManager::initialize()
                                                      "highlight_matches_in_page").toBool();
 
     initEditorConfigs();
+
+    initMarkdownConfigs();
 }
 
 void VConfigManager::initEditorConfigs()
 {
-    m_autoIndent = getConfigFromSettings("editor", "auto_indent").toBool();
+    const QString section("editor");
+
+    m_autoIndent = getConfigFromSettings(section, "auto_indent").toBool();
 
-    m_autoList = getConfigFromSettings("editor", "auto_list").toBool();
+    m_autoList = getConfigFromSettings(section, "auto_list").toBool();
 
-    m_autoQuote = getConfigFromSettings("editor", "auto_quote").toBool();
+    m_autoQuote = getConfigFromSettings(section, "auto_quote").toBool();
 
-    int keyMode = getConfigFromSettings("editor", "key_mode").toInt();
+    int keyMode = getConfigFromSettings(section, "key_mode").toInt();
     if (keyMode < 0 || keyMode >= (int)KeyMode::Invalid) {
         keyMode = 0;
     }
     m_keyMode = (KeyMode)keyMode;
 
-    m_enableSmartImInVimMode = getConfigFromSettings("editor",
+    m_enableSmartImInVimMode = getConfigFromSettings(section,
                                                      "enable_smart_im_in_vim_mode").toBool();
 
-    QString tmpLeader = getConfigFromSettings("editor",
+    QString tmpLeader = getConfigFromSettings(section,
                                               "vim_leader_key").toString();
     if (tmpLeader.isEmpty()) {
         m_vimLeaderKey = QChar(' ');
@@ -357,16 +361,22 @@ void VConfigManager::initEditorConfigs()
         }
     }
 
-    m_enableTabHighlight = getConfigFromSettings("editor",
+    m_enableTabHighlight = getConfigFromSettings(section,
                                                  "enable_tab_highlight").toBool();
 
-    m_parsePasteLocalImage = getConfigFromSettings("editor", "parse_paste_local_image").toBool();
+    m_parsePasteLocalImage = getConfigFromSettings(section, "parse_paste_local_image").toBool();
 
-    m_enableExtraBuffer = getConfigFromSettings("editor", "enable_extra_buffer").toBool();
+    m_enableExtraBuffer = getConfigFromSettings(section, "enable_extra_buffer").toBool();
 
-    m_autoScrollCursorLine = getConfigFromSettings("editor", "auto_scroll_cursor_line").toInt();
+    m_autoScrollCursorLine = getConfigFromSettings(section, "auto_scroll_cursor_line").toInt();
 
-    m_editorFontFamily = getConfigFromSettings("editor", "editor_font_family").toString();
+    m_editorFontFamily = getConfigFromSettings(section, "editor_font_family").toString();
+}
+
+void VConfigManager::initMarkdownConfigs()
+{
+    const QString section("markdown");
+    m_enableWavedrom = getConfigFromSettings(section, "enable_wavedrom").toBool();
 }
 
 void VConfigManager::initSettings()

+ 25 - 2
src/vconfigmanager.h

@@ -89,8 +89,6 @@ public:
 
     void initialize();
 
-    void initEditorConfigs();
-
     // Read config from the directory config json file into a QJsonObject.
     // @path is the directory containing the config json file.
     static QJsonObject readDirectoryConfig(const QString &path);
@@ -266,6 +264,9 @@ public:
     bool getEnableMathjax() const;
     void setEnableMathjax(bool p_enabled);
 
+    bool getEnableWavedrom() const;
+    void setEnableWavedrom(bool p_enabled);
+
     bool getEnableGraphviz() const;
     void setEnableGraphviz(bool p_enabled);
 
@@ -625,6 +626,10 @@ public:
     void setImageBrowsePath(const QString &p_path);
 
 private:
+    void initEditorConfigs();
+
+    void initMarkdownConfigs();
+
     // Look up a config from user and default settings.
     QVariant getConfigFromSettings(const QString &section, const QString &key) const;
 
@@ -782,6 +787,9 @@ private:
     // Enable Mathjax.
     bool m_enableMathjax;
 
+    // Enable WaveDrom.
+    bool m_enableWavedrom;
+
     // Enable Graphviz.
     bool m_enableGraphviz;
 
@@ -1605,6 +1613,21 @@ inline void VConfigManager::setEnableMathjax(bool p_enabled)
     setConfigToSettings("global", "enable_mathjax", m_enableMathjax);
 }
 
+inline bool VConfigManager::getEnableWavedrom() const
+{
+    return m_enableWavedrom;
+}
+
+inline void VConfigManager::setEnableWavedrom(bool p_enabled)
+{
+    if (m_enableWavedrom == p_enabled) {
+        return;
+    }
+
+    m_enableWavedrom = p_enabled;
+    setConfigToSettings("markdown", "enable_wavedrom", m_enableWavedrom);
+}
+
 inline bool VConfigManager::getEnableGraphviz() const
 {
     return m_enableGraphviz;

+ 4 - 0
src/vlivepreviewhelper.cpp

@@ -104,6 +104,7 @@ VLivePreviewHelper::VLivePreviewHelper(VEditor *p_editor,
 
     m_flowchartEnabled = g_config->getEnableFlowchart();
     m_mermaidEnabled = g_config->getEnableMermaid();
+    m_wavedromEnabled = g_config->getEnableWavedrom();
     m_plantUMLMode = g_config->getPlantUMLMode();
     m_graphvizEnabled = g_config->getEnableGraphviz();
     m_mathjaxEnabled = g_config->getEnableMathjax();
@@ -123,6 +124,9 @@ void VLivePreviewHelper::checkLang(const QString &p_lang,
 {
     if (m_flowchartEnabled && (p_lang == "flow" || p_lang == "flowchart")) {
         p_livePreview = p_inplacePreview = true;
+    } else if (m_wavedromEnabled && p_lang == "wavedrom") {
+        p_livePreview = true;
+        p_inplacePreview = false;
     } else if (m_plantUMLMode != PlantUMLMode::DisablePlantUML && p_lang == "puml") {
         p_livePreview = p_inplacePreview = true;
     } else if (m_graphvizEnabled && p_lang == "dot") {

+ 1 - 0
src/vlivepreviewhelper.h

@@ -282,6 +282,7 @@ private:
 
     bool m_flowchartEnabled;
     bool m_mermaidEnabled;
+    bool m_wavedromEnabled;
     int m_plantUMLMode;
     bool m_graphvizEnabled;
     bool m_mathjaxEnabled;

+ 46 - 29
src/vmainwindow.cpp

@@ -453,7 +453,7 @@ QToolBar *VMainWindow::initViewToolBar(QSize p_iconSize)
     viewMenu->addAction(menuBarAct);
 
     expandViewAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/expand.svg"),
-                                tr("Expand"),
+                                tr("Expand Edit Area"),
                                 this);
     VUtils::fixTextWithCaptainShortcut(expandViewAct, "ExpandMode");
     expandViewAct->setStatusTip(tr("Expand the edit area"));
@@ -933,34 +933,7 @@ void VMainWindow::initMarkdownMenu()
 
     markdownMenu->addSeparator();
 
-    QAction *mermaidAct = new QAction(tr("&Mermaid Diagram"), this);
-    mermaidAct->setToolTip(tr("Enable Mermaid for graph and diagram (re-open current tabs to make it work)"));
-    mermaidAct->setCheckable(true);
-    connect(mermaidAct, &QAction::triggered,
-            this, &VMainWindow::enableMermaid);
-    markdownMenu->addAction(mermaidAct);
-
-    mermaidAct->setChecked(g_config->getEnableMermaid());
-
-    QAction *flowchartAct = new QAction(tr("&Flowchart.js"), this);
-    flowchartAct->setToolTip(tr("Enable Flowchart.js for flowchart diagram (re-open current tabs to make it work)"));
-    flowchartAct->setCheckable(true);
-    connect(flowchartAct, &QAction::triggered,
-            this, [this](bool p_enabled){
-                g_config->setEnableFlowchart(p_enabled);
-                VUtils::promptForReopen(this);
-            });
-    markdownMenu->addAction(flowchartAct);
-    flowchartAct->setChecked(g_config->getEnableFlowchart());
-
-    QAction *mathjaxAct = new QAction(tr("Math&Jax"), this);
-    mathjaxAct->setToolTip(tr("Enable MathJax for math support in Markdown (re-open current tabs to make it work)"));
-    mathjaxAct->setCheckable(true);
-    connect(mathjaxAct, &QAction::triggered,
-            this, &VMainWindow::enableMathjax);
-    markdownMenu->addAction(mathjaxAct);
-
-    mathjaxAct->setChecked(g_config->getEnableMathjax());
+    initMarkdownExtensionMenu(markdownMenu);
 
     markdownMenu->addSeparator();
 
@@ -1683,6 +1656,50 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu)
     optMenu->addAction(emojiAct);
 }
 
+void VMainWindow::initMarkdownExtensionMenu(QMenu *p_menu)
+{
+    QMenu *optMenu = p_menu->addMenu(tr("Extensions"));
+    optMenu->setToolTipsVisible(true);
+
+    QAction *mermaidAct = new QAction(tr("&Mermaid Diagram"), optMenu);
+    mermaidAct->setToolTip(tr("Enable Mermaid for graph and diagram (re-open current tabs to make it work)"));
+    mermaidAct->setCheckable(true);
+    mermaidAct->setChecked(g_config->getEnableMermaid());
+    connect(mermaidAct, &QAction::triggered,
+            this, &VMainWindow::enableMermaid);
+    optMenu->addAction(mermaidAct);
+
+    QAction *flowchartAct = new QAction(tr("&Flowchart.js"), optMenu);
+    flowchartAct->setToolTip(tr("Enable Flowchart.js for flowchart diagram (re-open current tabs to make it work)"));
+    flowchartAct->setCheckable(true);
+    flowchartAct->setChecked(g_config->getEnableFlowchart());
+    connect(flowchartAct, &QAction::triggered,
+            this, [this](bool p_enabled){
+                g_config->setEnableFlowchart(p_enabled);
+                VUtils::promptForReopen(this);
+            });
+    optMenu->addAction(flowchartAct);
+
+    QAction *mathjaxAct = new QAction(tr("Math&Jax"), optMenu);
+    mathjaxAct->setToolTip(tr("Enable MathJax for math support in Markdown (re-open current tabs to make it work)"));
+    mathjaxAct->setCheckable(true);
+    mathjaxAct->setChecked(g_config->getEnableMathjax());
+    connect(mathjaxAct, &QAction::triggered,
+            this, &VMainWindow::enableMathjax);
+    optMenu->addAction(mathjaxAct);
+
+    QAction *wavedromAct = new QAction(tr("&WaveDrom"), optMenu);
+    wavedromAct->setToolTip(tr("Enable WaveDrom for digital timing diagram (re-open current tabs to make it work)"));
+    wavedromAct->setCheckable(true);
+    wavedromAct->setChecked(g_config->getEnableWavedrom());
+    connect(wavedromAct, &QAction::triggered,
+            this, [this](bool p_enabled){
+                g_config->setEnableWavedrom(p_enabled);
+                VUtils::promptForReopen(this);
+            });
+    optMenu->addAction(wavedromAct);
+}
+
 void VMainWindow::initRenderBackgroundMenu(QMenu *menu)
 {
     QActionGroup *renderBackgroundAct = new QActionGroup(this);

+ 4 - 0
src/vmainwindow.h

@@ -258,7 +258,11 @@ private:
     void initCodeBlockStyleMenu(QMenu *p_menu);
 
     void initConverterMenu(QMenu *p_menu);
+
     void initMarkdownitOptionMenu(QMenu *p_menu);
+
+    void initMarkdownExtensionMenu(QMenu *p_menu);
+
     void initEditorBackgroundMenu(QMenu *menu);
 
     // Init the Line Number submenu in Edit menu.

+ 3 - 0
src/vnote.cpp

@@ -58,6 +58,9 @@ const QString VNote::c_mermaidForestCssFile = ":/utils/mermaid/mermaid.forest.cs
 const QString VNote::c_flowchartJsFile = ":/utils/flowchart.js/flowchart.min.js";
 const QString VNote::c_raphaelJsFile = ":/utils/flowchart.js/raphael.min.js";
 
+const QString VNote::c_wavedromJsFile = ":/utils/wavedrom/wavedrom.min.js";
+const QString VNote::c_wavedromThemeFile = ":/utils/wavedrom/wavedrom-theme.js";
+
 const QString VNote::c_plantUMLJsFile = "http://s.plantuml.com/synchro2.js";
 const QString VNote::c_plantUMLZopfliJsFile = "http://s.plantuml.com/zopfli.raw.min.js";
 

+ 4 - 0
src/vnote.h

@@ -73,6 +73,10 @@ public:
     static const QString c_flowchartJsFile;
     static const QString c_raphaelJsFile;
 
+    // WaveDrom
+    static const QString c_wavedromJsFile;
+    static const QString c_wavedromThemeFile;
+
     // PlantUML
     static const QString c_plantUMLJsFile;
     static const QString c_plantUMLZopfliJsFile;

+ 2 - 0
src/vnote.qrc

@@ -278,5 +278,7 @@
         <file>resources/icons/256x256/vnote.png</file>
         <file>utils/markdown-it/markdown-it-container.min.js</file>
         <file>resources/icons/table.svg</file>
+        <file>utils/wavedrom/wavedrom.min.js</file>
+        <file>utils/wavedrom/wavedrom-theme.js</file>
     </qresource>
 </RCC>

Some files were not shown because too many files changed in this diff