Browse Source

LivePreview: smart live preview for sequence diagram

Le Tan 7 năm trước cách đây
mục cha
commit
2bf36319d1
2 tập tin đã thay đổi với 194 bổ sung35 xóa
  1. 13 7
      src/resources/markdown_template.js
  2. 181 28
      src/vplantumlhelper.cpp

+ 13 - 7
src/resources/markdown_template.js

@@ -1421,6 +1421,8 @@ var setPreviewEnabled = function(enabled) {
         previewDiv.style.display = 'none';
         previewDiv.innerHTML = '';
     }
+
+    clearMarkRectDivs();
 };
 
 var previewCodeBlock = function(id, lang, text, isLivePreview) {
@@ -1737,17 +1739,12 @@ var findNodeWithText = function(node, text, isMatched) {
 
 // Draw a rectangle to mark @rect.
 var markNode = function(rect) {
+    clearMarkRectDivs();
+
     if (!rect) {
         return;
     }
 
-    var nodes = document.getElementsByClassName(VMarkRectDivClass);
-    while (nodes.length > 0) {
-        var n = nodes[0];
-        n.outerHTML = '';
-        delete n;
-    }
-
     var div = document.createElement('div');
     div.id = 'markrect_' + Date.now();
     div.classList.add(VMarkRectDivClass);
@@ -1765,3 +1762,12 @@ var markNode = function(rect) {
                + 'if (node) { node.outerHTML = ""; delete node; }',
                3000);
 };
+
+var clearMarkRectDivs = function() {
+    var nodes = document.getElementsByClassName(VMarkRectDivClass);
+    while (nodes.length > 0) {
+        var n = nodes[0];
+        n.outerHTML = '';
+        delete n;
+    }
+};

+ 181 - 28
src/vplantumlhelper.cpp

@@ -193,21 +193,21 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
 {
     Q_UNUSED(p_isRegex);
 
-    // class ABC #Pink {
-    static QRegExp classDef1("^\\s*(?:class|(?:abstract(?:\\s+class)?)|interface|annotation|enum)\\s*"
-                             "(?!class)(\\w+)\\s*.*");
+    // class ABC #Pink
+    static QRegExp classDef1("^\\s*(?:class|(?:abstract(?:\\s+class)?)|interface|annotation|enum)\\s+"
+                             "(?!class)(\\w+)");
     if (classDef1.indexIn(p_keyword) >= 0) {
         p_keyword = classDef1.cap(1);
         p_hints = "id";
         return true;
     }
 
-    // class "ABC DEF" as AD #Pink {
-    static QRegExp classDef2("^\\s*(?:class|(?:abstract(?:\\s+class)?)|interface|annotation|enum)\\s*"
-                             "\"([^\"]+)\"\\s*(?:\\bas (\\w+))?.*");
+    // class "ABC DEF" as AD #Pink
+    static QRegExp classDef2("^\\s*(?:class|(?:abstract(?:\\s+class)?)|interface|annotation|enum)\\s+"
+                             "\"([^\"]+)\"\\s*(?:\\bas\\s+(\\w+))?");
     if (classDef2.indexIn(p_keyword) >= 0) {
         if (classDef2.cap(2).isEmpty()) {
-            p_keyword = classDef2.cap(1);
+            p_keyword = classDef2.cap(1).trimmed();
         } else {
             p_keyword = classDef2.cap(2);
         }
@@ -216,24 +216,23 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
     }
 
     // class01 "1" *-- "many" class02 : contains 4 >
-    static QRegExp relation("^\\s*(?:(\\w+)|\"([^\"]+)\")\\s+"
-                            "(?:\"[^\"]+\"\\s+)?"
-                            "(?:<\\||[*o<#x}+^])?" "(?:-+|\\.+)" "(?:\\|>|[*o>#x{+^])?\\s+"
-                            "(?:\"[^\"]+\"\\s+)?"
+    static QRegExp relation("^\\s*(?:(\\w+)|\"([^\"]+)\")\\s*"
+                            "(?:\"[^\"]+\"\\s*)?"
+                            "(?:<\\||[*o<#x}+^])?" "(?:-+|\\.+)" "(?:\\|>|[*o>#x{+^])?\\s*"
+                            "(?:\"[^\"]+\"\\s*)?"
                             "(?:(\\w+)|\"([^\"]+)\")\\s*"
                             "(?::(.+))?");
     if (relation.indexIn(p_keyword) >= 0) {
-        QString note(relation.cap(5));
-        if (note.isEmpty()) {
+        if (relation.cap(5).isEmpty()) {
             QString class2 = relation.cap(3);
             if (class2.isEmpty()) {
-                class2 = relation.cap(4);
+                class2 = relation.cap(4).trimmed();
             }
 
             p_keyword = class2;
             p_hints = "id";
         } else {
-            p_keyword = note.trimmed();
+            p_keyword = relation.cap(5).trimmed();
         }
 
         return true;
@@ -257,7 +256,7 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
         return true;
     }
 
-    // note left on link: message
+    // note left on link #Pink : message
     // note left on link
     // node on link: message
     // MUST before next rule "note".
@@ -270,11 +269,13 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
         return true;
     }
 
-    // note top of Object: message
+    // note top of Object #Pink : message
     // note top of Object
     // note top: message
-    static QRegExp note("^\\s*note\\s+(?:left|top|right|bottom)"
-                        "(?:\\s+of\\s+(\\w+))?\\s*"
+    // hnote and rnote for sequence diagram.
+    static QRegExp note("^\\s*[hr]?note\\s+(?:left|top|right|bottom)"
+                        "(?:\\s+of\\s+(\\w+))?"
+                        "[^:]*"
                         "(?::(.*))?");
     if (note.indexIn(p_keyword) >= 0) {
         p_keyword = note.cap(2).trimmed();
@@ -288,16 +289,16 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
         return true;
     }
 
-    // note "a floating note" as N1
+    // note "a floating note" as N1 #Pink
     // note as N1
-    static QRegExp note2("^\\s*note\\s+(?:\"([^\"]*)\"\\s+)?as\\s+\\w+\\s*");
+    static QRegExp note2("^\\s*note\\s+(?:\"([^\"]*)\"\\s+)?as\\s+\\w+");
     if (note2.indexIn(p_keyword) >= 0) {
-        p_keyword = note2.cap(1);
+        p_keyword = note2.cap(1).trimmed();
         return true;
     }
 
     // end note
-    static QRegExp note3("^\\s*end note\\s*$");
+    static QRegExp note3("^\\s*end ?note\\s*$");
     if (note3.indexIn(p_keyword) >= 0) {
         p_keyword.clear();
         return true;
@@ -355,17 +356,26 @@ static bool tryActivityDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
 
     // Activity.
     // multiple lines;
-    static QRegExp activity2("^\\s*(.+)[;|<>/\\]}]\\s*$");
+    static QRegExp activity2("^\\s*(.+)([;|<>/\\]}])\\s*$");
     if (activity2.indexIn(p_keyword) >= 0) {
-        p_keyword = activity2.cap(1).trimmed();
+        QString word = activity2.cap(1);
+        QChar end = activity2.cap(2)[0];
+        if (end != ';' && !word.isEmpty()) {
+            // || << >> // ]] }} are not legal.
+            if (word[word.size() - 1] == end) {
+                return false;
+            }
+        }
+
+        p_keyword = word.trimmed();
         return true;
     }
 
     // start, stop, end, endif, repeat, fork, fork again, end fork, },
     // detach
-    static QRegExp start("^\\s*(?:start|stop|end|endif|repeat|"
-                         "fork(?:\\s+again)?|end\\s+fork|\\}|detach)\\s*$");
-    if (start.indexIn(p_keyword) >= 0) {
+    static QRegExp keywords("^\\s*(?:start|stop|end|endif|repeat|"
+                            "fork(?:\\s+again)?|end\\s+fork|\\}|detach)\\s*$");
+    if (keywords.indexIn(p_keyword) >= 0) {
         p_keyword.clear();
         return true;
     }
@@ -426,6 +436,143 @@ static bool tryActivityDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
     return false;
 }
 
+static bool trySequenceDiagram(QString &p_keyword, QString &p_hints, bool &p_isRegex)
+{
+    Q_UNUSED(p_isRegex);
+    Q_UNUSED(p_hints);
+
+    // participant ABC #Pink
+    // participant "ABC DEF" as AD #Pink
+    static QRegExp participant1("^\\s*(?:participant|actor|boundary|control|entity|database)\\s+"
+                                "(?:(\\w+)|\"([^\"]+)\"\\s*(?:\\bas\\s+\\w+)?)");
+    if (participant1.indexIn(p_keyword) >= 0) {
+        p_keyword = participant1.cap(1);
+        if (p_keyword.isEmpty()) {
+            p_keyword = participant1.cap(2).trimmed();
+        }
+
+        return true;
+    }
+
+    // "abc" ->> "def" : Authentication
+    static QRegExp message("^\\s*(?:\\w+|\"[^\"]+\")\\s*"
+                           "[-<>x\\\\/o]+\\s*"
+                           "(?:\\w+|\"[^\"]+\")\\s*"
+                           ":\\s*(.+)");
+    if (message.indexIn(p_keyword) >= 0) {
+        p_keyword = message.cap(1).trimmed();
+        return true;
+    }
+
+    // autonumber
+    static QRegExp autonum("^\\s*autonumber\\s+");
+    if (autonum.indexIn(p_keyword) >= 0) {
+        p_keyword.clear();
+        return true;
+    }
+
+    // newpage
+    static QRegExp newpage("^\\s*newpage\\s+(.+)");
+    if (newpage.indexIn(p_keyword) >= 0) {
+        p_keyword = newpage.cap(1).trimmed();
+        return true;
+    }
+
+    // alt, else, group, loop ABCDEFG
+    static QRegExp group1("^\\s*(?:alt|else|group|loop)\\s+(.*)");
+    if (group1.indexIn(p_keyword) >= 0) {
+        p_keyword = group1.cap(1).trimmed();
+        return true;
+    }
+
+    // note over bob, alice #Pink:
+    // ret over bob, alice : init
+    static QRegExp noteon("^\\s*(?:[hr]?note|ref)\\s+over\\s+"
+                          "(\\w+)[^:]*"
+                          "(?::(.+))?");
+    if (noteon.indexIn(p_keyword) >= 0) {
+        p_keyword = noteon.cap(2).trimmed();
+        if (p_keyword.isEmpty()) {
+            p_keyword = noteon.cap(1);
+        }
+
+        return true;
+    }
+
+    // Divider.
+    // == Initialization ==
+    static QRegExp divider("^\\s*==\\s*([^=]*)==\\s*$");
+    if (divider.indexIn(p_keyword) >= 0) {
+        p_keyword = divider.cap(1).trimmed();
+        return true;
+    }
+
+    // Delay.
+    // ... 5 minutes latter ...
+    static QRegExp delay("^\\s*\\.\\.\\.(?:(.+)\\.\\.\\.)?\\s*$");
+    if (delay.indexIn(p_keyword) >= 0) {
+        p_keyword = delay.cap(1).trimmed();
+        return true;
+    }
+
+    // activate A
+    static QRegExp activate("^\\s*(?:(?:de)?activate|destroy)\\s+"
+                            "(?:(\\w+)|\"([^\"]+)\")");
+    if (activate.indexIn(p_keyword) >= 0) {
+        p_keyword = activate.cap(1);
+        if (p_keyword.isEmpty()) {
+            p_keyword = activate.cap(2).trimmed();
+        }
+
+        return true;
+    }
+
+    // create control ABC
+    static QRegExp create("^\\s*create\\s+(?:\\w+\\s+)?"
+                          "(?:(\\w+)|\"([^\"]+)\")");
+    if (create.indexIn(p_keyword) >= 0) {
+        p_keyword = create.cap(1);
+        if (p_keyword.isEmpty()) {
+            p_keyword = create.cap(2).trimmed();
+        }
+
+        return true;
+    }
+
+    // Incoming and outgoing message.
+    static QRegExp incoming("^\\s*\\[[-<>ox]+\\s*"
+                            "(?:\\w+|\"[^\"]+\")\\s*"
+                            ":\\s*(.+)");
+    if (incoming.indexIn(p_keyword) >= 0) {
+        p_keyword = incoming.cap(1).trimmed();
+        return true;
+    }
+
+    static QRegExp outgoing("^\\s*(?:\\w+|\"[^\"]+\")\\s*"
+                            "[-<>ox]+\\]\\s*"
+                            ":\\s*(.+)");
+    if (outgoing.indexIn(p_keyword) >= 0) {
+        p_keyword = outgoing.cap(1).trimmed();
+        return true;
+    }
+
+    // box "Internal Service" #Pink
+    static QRegExp box("^\\s*box(?:\\s+\"([^\"]+)\")?\\s*");
+    if (box.indexIn(p_keyword) >= 0) {
+        p_keyword = box.cap(1).trimmed();
+        return true;
+    }
+
+    // end box
+    static QRegExp endbox("^\\s*end ?box\\s*$");
+    if (endbox.indexIn(p_keyword) >= 0) {
+        p_keyword.clear();
+        return true;
+    }
+
+    return false;
+}
+
 QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text,
                                                     QString &p_hints,
                                                     bool &p_isRegex)
@@ -449,6 +596,12 @@ QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text,
         return kw;
     }
 
+    qDebug() << "trySequenceDiagram" << kw;
+
+    if (trySequenceDiagram(kw, p_hints, p_isRegex)) {
+        return kw;
+    }
+
     qDebug() << "tryCommonElements" << kw;
 
     if (tryCommonElements(kw, p_hints, p_isRegex)) {