Browse Source

LivePreview: support UseCase diagram

Le Tan 7 years ago
parent
commit
774eec32d7
2 changed files with 155 additions and 29 deletions
  1. 3 1
      src/resources/markdown_template.js
  2. 152 28
      src/vplantumlhelper.cpp

+ 3 - 1
src/resources/markdown_template.js

@@ -1653,7 +1653,9 @@ var performSmartLivePreview = function(lang, text, hints, isRegex) {
                                           return null;
                                       });
         targetNode = result.node;
-    } else {
+    }
+
+    if (!targetNode) {
         var result;
         if (isRegex) {
             var nodeReg = new RegExp(text);

+ 152 - 28
src/vplantumlhelper.cpp

@@ -189,6 +189,26 @@ QByteArray VPlantUMLHelper::process(const QString &p_format, const QString &p_te
     return out;
 }
 
+static bool tryKeywords(QString &p_keyword)
+{
+    // start, stop, end, endif, repeat, fork, fork again, end fork, },
+    // detach, end note, end box, endrnote, endhnote,
+    // top to bottom direction, left to right direction,
+    // @startuml, @enduml
+    static QRegExp keywords("^\\s*(?:start|stop|end|endif|repeat|"
+                            "fork(?:\\s+again)?|end\\s+fork|\\}|detach|"
+                            "end ?(?:note|box)|endrnote|endhnote|"
+                            "top\\s+to\\s+bottom\\s+direction|"
+                            "left\\s+to\\s+right\\s+direction|"
+                            "@startuml|@enduml)\\s*$");
+    if (keywords.indexIn(p_keyword) >= 0) {
+        p_keyword.clear();
+        return true;
+    }
+
+    return false;
+}
+
 static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRegex, bool &p_needCreole)
 {
     Q_UNUSED(p_isRegex);
@@ -275,16 +295,20 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
     // note top of Object
     // note top: message
     // hnote and rnote for sequence diagram.
+    // note right of (use case)
     static QRegExp note("^\\s*[hr]?note\\s+(?:left|top|right|bottom)"
-                        "(?:\\s+of\\s+(\\w+))?"
+                        "(?:\\s+of\\s+(?:(\\w+)|\\(([^\\)]+)\\)))?"
                         "[^:]*"
                         "(?::(.*))?");
     if (note.indexIn(p_keyword) >= 0) {
-        p_keyword = note.cap(2).trimmed();
+        p_keyword = note.cap(3).trimmed();
         if (p_keyword.isEmpty()) {
-            p_keyword = note.cap(1);
-            if (!p_keyword.isEmpty()) {
+            p_keyword = note.cap(2).trimmed();
+            if (p_keyword.isEmpty()) {
+                p_keyword = note.cap(1);
                 p_hints = "id";
+            } else {
+                p_needCreole = true;
             }
         } else {
             p_needCreole = true;
@@ -301,13 +325,6 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
         return true;
     }
 
-    // end note
-    static QRegExp note3("^\\s*end ?note\\s*$");
-    if (note3.indexIn(p_keyword) >= 0) {
-        p_keyword.clear();
-        return true;
-    }
-
     return false;
 }
 
@@ -336,7 +353,7 @@ static bool tryActivityDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
     // Activity. (Do not support color.)
     // :Hello world;
     // :Across multiple lines
-    static QRegExp activity1("^\\s*:(.+)\\s*$");
+    static QRegExp activity1("^\\s*:([^:]+)\\s*$");
     if (activity1.indexIn(p_keyword) >= 0) {
         p_keyword = activity1.cap(1).trimmed();
         if (!p_keyword.isEmpty()) {
@@ -370,15 +387,6 @@ static bool tryActivityDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
         return true;
     }
 
-    // start, stop, end, endif, repeat, fork, fork again, end fork, },
-    // detach
-    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;
-    }
-
     // Conditionals.
     // if (Graphviz) then (yes)
     // else if (Graphviz) then (yes)
@@ -454,8 +462,8 @@ static bool trySequenceDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
     }
 
     // "abc" ->> "def" : Authentication
-    static QRegExp message("^\\s*(?:\\w+|\"[^\"]+\")\\s*"
-                           "[-<>x\\\\/o]+\\s*"
+    static QRegExp message("^\\s*(?:\\w+|\"[^\"]+\")\\s+"
+                           "[-<>x\\\\/o]{2,}\\s+"
                            "(?:\\w+|\"[^\"]+\")\\s*"
                            ":\\s*(.+)");
     if (message.indexIn(p_keyword) >= 0) {
@@ -486,7 +494,7 @@ static bool trySequenceDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
     }
 
     // note over bob, alice #Pink:
-    // ret over bob, alice : init
+    // ref over bob, alice : init
     static QRegExp noteon("^\\s*(?:[hr]?note|ref)\\s+over\\s+"
                           "(\\w+)[^:]*"
                           "(?::(.+))?");
@@ -567,10 +575,102 @@ static bool trySequenceDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
         return true;
     }
 
-    // end box
-    static QRegExp endbox("^\\s*end ?box\\s*$");
-    if (endbox.indexIn(p_keyword) >= 0) {
-        p_keyword.clear();
+    return false;
+}
+
+static bool tryUseCaseDiagram(QString &p_keyword, QString &p_hints, bool &p_isRegex, bool &p_needCreole)
+{
+    Q_UNUSED(p_isRegex);
+    Q_UNUSED(p_hints);
+
+    // User -> (Start)
+    // User --> (Use the application) : A small label
+    // :Main Admin: --> (Use the application) : This is another label
+    // (chekckout) -- (payment) : include
+    static QRegExp rel("^\\s*(?:(\\w+)|:([^:]+):|\\(([^\\)]+)\\))\\s*"
+                       "[-.<>]{2,}\\s*"
+                       "(?:\\(([^\\)]+)\\)|(\\w+))\\s*"
+                       "(?::(.+))?");
+    if (rel.indexIn(p_keyword) >= 0) {
+        QString msg(rel.cap(6).trimmed());
+        if (msg.isEmpty()) {
+            QString ent2(rel.cap(4).trimmed());
+            if (ent2.isEmpty()) {
+                ent2 = rel.cap(5).trimmed();
+            }
+
+            if (ent2.isEmpty()) {
+                QString ent1(rel.cap(3).trimmed());
+                if (ent1.isEmpty()) {
+                    ent1 = rel.cap(2).trimmed();
+                    if (ent1.isEmpty()) {
+                        ent1 = rel.cap(1).trimmed();
+                    }
+                }
+
+                p_keyword = ent1;
+            } else {
+                p_keyword = ent2;
+            }
+        } else {
+            p_keyword = msg;
+        }
+
+        p_needCreole = true;
+        return true;
+    }
+
+    // Usecase.
+    // (First usecase) as (UC2)
+    // usecase UC3
+    // usecase (Last usecase) as UC4
+    static QRegExp usecase1("^\\s*usecase\\s+(?:(\\w+)|\\(([^\\)]+)\\)\\s+as\\s+\\w+)");
+    if (usecase1.indexIn(p_keyword) >= 0) {
+        if (usecase1.cap(1).isEmpty()) {
+            p_keyword = usecase1.cap(2).trimmed();
+            p_needCreole = true;
+        } else {
+            p_keyword = usecase1.cap(1);
+        }
+
+        return true;
+    }
+
+    // This will eat almost anything starting with ().
+    static QRegExp usecase2("^\\s*\\(([^\\)]+)\\)");
+    if (usecase2.indexIn(p_keyword) >= 0) {
+        p_keyword = usecase2.cap(1).trimmed();
+        p_needCreole = true;
+        return true;
+    }
+
+    // Actor.
+    // :Another\nactor: as men2
+    // actor :Last actor: as men4
+    static QRegExp actor1("^\\s*(?:actor\\s+)?:([^:]+):(?:\\s+as\\s+\\w+)?[^:]*$");
+    if (actor1.indexIn(p_keyword) >= 0) {
+        p_keyword = actor1.cap(1).trimmed();
+        p_needCreole = true;
+        return true;
+    }
+
+    // actor men1
+    static QRegExp actor2("^\\s*actor\\s+(\\w+)");
+    if (actor2.indexIn(p_keyword) >= 0) {
+        p_keyword = actor2.cap(1).trimmed();
+        p_needCreole = true;
+        return true;
+    }
+
+    // rectangle checkout {
+    static QRegExp rect("^\\s*rectangle\\s+(?:(\\w+)|\"([^\"]+)\")");
+    if (rect.indexIn(p_keyword) >= 0) {
+        p_keyword = rect.cap(1).trimmed();
+        if (p_keyword.isEmpty()) {
+            p_keyword = rect.cap(2).trimmed();
+        }
+
+        p_needCreole = true;
         return true;
     }
 
@@ -607,6 +707,14 @@ QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text,
     p_isRegex = false;
     bool needCreole = false;
 
+    qDebug() << "tryKeywords" << kw;
+
+    if (tryKeywords(kw)) {
+        return kw;
+    }
+
+    qDebug() << "tryClassDiagram" << kw;
+
     if (tryClassDiagram(kw, p_hints, p_isRegex, needCreole)) {
         if (needCreole) {
             goto creole;
@@ -615,6 +723,8 @@ QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text,
         return kw;
     }
 
+    qDebug() << "tryActivityDiagram" << kw;
+
     if (tryActivityDiagram(kw, p_hints, p_isRegex, needCreole)) {
         if (needCreole) {
             goto creole;
@@ -623,6 +733,8 @@ QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text,
         return kw;
     }
 
+    qDebug() << "trySequenceDiagram" << kw;
+
     if (trySequenceDiagram(kw, p_hints, p_isRegex, needCreole)) {
         if (needCreole) {
             goto creole;
@@ -631,6 +743,18 @@ QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text,
         return kw;
     }
 
+    qDebug() << "tryUseCaseDiagram" << kw;
+
+    if (tryUseCaseDiagram(kw, p_hints, p_isRegex, needCreole)) {
+        if (needCreole) {
+            goto creole;
+        }
+
+        return kw;
+    }
+
+    qDebug() << "tryCommonElements" << kw;
+
     if (tryCommonElements(kw, p_hints, p_isRegex, needCreole)) {
         if (needCreole) {
             goto creole;