Selaa lähdekoodia

LivePreview: search for multiple tokens and select the best match

Le Tan 7 vuotta sitten
vanhempi
sitoutus
74ec3884d0
2 muutettua tiedostoa jossa 130 lisäystä ja 48 poistoa
  1. 76 27
      src/resources/markdown_template.js
  2. 54 21
      src/vplantumlhelper.cpp

+ 76 - 27
src/resources/markdown_template.js

@@ -1638,30 +1638,61 @@ var performSmartLivePreview = function(lang, text, hints, isRegex) {
     var targetNode = null;
     if (hints.indexOf('id') >= 0) {
         // isRegex is ignored.
-        targetNode = findNodeWithText(previewDiv,
+        var result = findNodeWithText(previewDiv,
                                       text,
                                       function (node, text) {
-                                          if (!node.id) {
-                                              return false;
+                                          if (node.id && node.id == text) {
+                                              var res = { stop: true,
+                                                  node: { node: node,
+                                                      diff: 0
+                                                  }
+                                              };
+                                              return res;
                                           }
 
-                                          return node.id == text;
+                                          return null;
                                       });
+        targetNode = result.node;
     } else {
+        var result;
         if (isRegex) {
             var nodeReg = new RegExp(text);
-            targetNode = findNodeWithText(previewDiv,
-                                          text,
-                                          function(node, text) {
-                                              return nodeReg.test(node.textContent);
-                                          });
+            result = findNodeWithText(previewDiv,
+                                      text,
+                                      function(node, text) {
+                                          var se = nodeReg.exec(node.textContent);
+                                          if (!se) {
+                                              return null;
+                                          }
+
+                                          var diff = node.textContent.length - se[0].length;
+                                          var res = { stop: diff == 0,
+                                              node: { node: node,
+                                                  diff: diff
+                                              }
+                                          };
+                                          return res;
+                                      });
         } else {
-            targetNode = findNodeWithText(previewDiv,
-                                          text,
-                                          function(node, text) {
-                                              return node.textContent.indexOf(text) >= 0;
-                                          });
+            result = findNodeWithText(previewDiv,
+                                      text,
+                                      function(node, text) {
+                                          var idx = node.textContent.indexOf(text);
+                                          if (idx < 0) {
+                                              return null;
+                                          }
+
+                                          var diff = node.textContent.length - text.length;
+                                          var res = { stop: diff == 0,
+                                              node: { node: node,
+                                                  diff: diff
+                                              }
+                                          };
+                                          return res;
+                                      });
         }
+
+        targetNode = result.node;
     }
 
     if (!targetNode) {
@@ -1713,28 +1744,46 @@ var performSmartLivePreview = function(lang, text, hints, isRegex) {
     markNode(nrect);
 }
 
+// isMatched() should return a strut or null:
+// - null to indicates a mismatch;
+// - { stop: whether continue search,
+//     node: { node: the matched node,
+//             diff: a value indicates the match quality (the lower the better)
+//           }
+//   }
 var findNodeWithText = function(node, text, isMatched) {
+    var result = {
+        node: null,
+        diff: 999999
+    };
+
+    findNodeWithTextInternal(node, text, isMatched, result);
+    return result;
+}
+
+// Return true to stop search.
+var findNodeWithTextInternal = function(node, text, isMatched, result) {
     var children = node.children;
-    if (children.length == 0) {
-        if (isMatched(node, text)) {
-            return node;
-        } else {
-            return null;
+    if (children.length > 0) {
+        for (var i = 0; i < children.length; ++i) {
+            var ret = findNodeWithTextInternal(children[i], text, isMatched, result);
+            if (ret) {
+                return ret;
+            }
         }
     }
 
-    for (var i = 0; i < children.length; ++i) {
-        var ret = findNodeWithText(children[i], text, isMatched);
-        if (ret) {
-            return ret;
+    var res = isMatched(node, text);
+    if (res) {
+        if (res.node.diff < result.diff) {
+            result.node = res.node.node;
+            result.diff = res.node.diff;
         }
-    }
 
-    if (isMatched(node, text)) {
-        return node;
+        return res.stop;
     }
 
-    return null;
+    return false;
 }
 
 // Draw a rectangle to mark @rect.

+ 54 - 21
src/vplantumlhelper.cpp

@@ -189,7 +189,7 @@ QByteArray VPlantUMLHelper::process(const QString &p_format, const QString &p_te
     return out;
 }
 
-static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRegex)
+static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRegex, bool &p_needCreole)
 {
     Q_UNUSED(p_isRegex);
 
@@ -232,6 +232,7 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
             p_keyword = class2;
             p_hints = "id";
         } else {
+            p_needCreole = true;
             p_keyword = relation.cap(5).trimmed();
         }
 
@@ -265,6 +266,7 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
                          "[^:]*"
                          "(?::(.*))?");
     if (note4.indexIn(p_keyword) >= 0) {
+        p_needCreole = true;
         p_keyword = note4.cap(1).trimmed();
         return true;
     }
@@ -284,6 +286,8 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
             if (!p_keyword.isEmpty()) {
                 p_hints = "id";
             }
+        } else {
+            p_needCreole = true;
         }
 
         return true;
@@ -307,19 +311,11 @@ static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRege
     return false;
 }
 
-static bool tryCommonElements(QString &p_keyword, QString &p_hints, bool &p_isRegex)
+static bool tryCommonElements(QString &p_keyword, QString &p_hints, bool &p_isRegex, bool &p_needCreole)
 {
     Q_UNUSED(p_isRegex);
     Q_UNUSED(p_hints);
-
-    // List.
-    // ** list
-    // # list
-    static QRegExp listMark("^\\s*(?:\\*+|#+)\\s+(.+)$");
-    if (listMark.indexIn(p_keyword) >= 0) {
-        p_keyword = listMark.cap(1).trimmed();
-        return true;
-    }
+    Q_UNUSED(p_needCreole);
 
     // Words in quotes.
     // cmf("abc")
@@ -332,7 +328,7 @@ static bool tryCommonElements(QString &p_keyword, QString &p_hints, bool &p_isRe
     return false;
 }
 
-static bool tryActivityDiagram(QString &p_keyword, QString &p_hints, bool &p_isRegex)
+static bool tryActivityDiagram(QString &p_keyword, QString &p_hints, bool &p_isRegex, bool &p_needCreole)
 {
     Q_UNUSED(p_isRegex);
     Q_UNUSED(p_hints);
@@ -351,6 +347,7 @@ static bool tryActivityDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
             }
         }
 
+        p_needCreole = true;
         return true;
     }
 
@@ -367,6 +364,8 @@ static bool tryActivityDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
             }
         }
 
+        // It may conflict with note.
+        p_needCreole = true;
         p_keyword = word.trimmed();
         return true;
     }
@@ -436,7 +435,7 @@ 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)
+static bool trySequenceDiagram(QString &p_keyword, QString &p_hints, bool &p_isRegex, bool &p_needCreole)
 {
     Q_UNUSED(p_isRegex);
     Q_UNUSED(p_hints);
@@ -460,6 +459,7 @@ static bool trySequenceDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
                            "(?:\\w+|\"[^\"]+\")\\s*"
                            ":\\s*(.+)");
     if (message.indexIn(p_keyword) >= 0) {
+        p_needCreole = true;
         p_keyword = message.cap(1).trimmed();
         return true;
     }
@@ -494,6 +494,8 @@ static bool trySequenceDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
         p_keyword = noteon.cap(2).trimmed();
         if (p_keyword.isEmpty()) {
             p_keyword = noteon.cap(1);
+        } else {
+            p_needCreole = true;
         }
 
         return true;
@@ -544,6 +546,7 @@ static bool trySequenceDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
                             "(?:\\w+|\"[^\"]+\")\\s*"
                             ":\\s*(.+)");
     if (incoming.indexIn(p_keyword) >= 0) {
+        p_needCreole = true;
         p_keyword = incoming.cap(1).trimmed();
         return true;
     }
@@ -552,6 +555,7 @@ static bool trySequenceDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
                             "[-<>ox]+\\]\\s*"
                             ":\\s*(.+)");
     if (outgoing.indexIn(p_keyword) >= 0) {
+        p_needCreole = true;
         p_keyword = outgoing.cap(1).trimmed();
         return true;
     }
@@ -573,6 +577,24 @@ static bool trySequenceDiagram(QString &p_keyword, QString &p_hints, bool &p_isR
     return false;
 }
 
+static bool tryCreole(QString &p_keyword)
+{
+    if (p_keyword.isEmpty()) {
+        return false;
+    }
+
+    // List.
+    // ** list
+    // # list
+    static QRegExp listMark("^\\s*(?:\\*+|#+)\\s+(.+)$");
+    if (listMark.indexIn(p_keyword) >= 0) {
+        p_keyword = listMark.cap(1).trimmed();
+        return true;
+    }
+
+    return false;
+}
+
 QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text,
                                                     QString &p_hints,
                                                     bool &p_isRegex)
@@ -583,30 +605,41 @@ QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text,
     }
 
     p_isRegex = false;
+    bool needCreole = false;
 
-    qDebug() << "tryClassDiagram" << kw;
+    if (tryClassDiagram(kw, p_hints, p_isRegex, needCreole)) {
+        if (needCreole) {
+            goto creole;
+        }
 
-    if (tryClassDiagram(kw, p_hints, p_isRegex)) {
         return kw;
     }
 
-    qDebug() << "tryActivityDiagram" << kw;
+    if (tryActivityDiagram(kw, p_hints, p_isRegex, needCreole)) {
+        if (needCreole) {
+            goto creole;
+        }
 
-    if (tryActivityDiagram(kw, p_hints, p_isRegex)) {
         return kw;
     }
 
-    qDebug() << "trySequenceDiagram" << kw;
+    if (trySequenceDiagram(kw, p_hints, p_isRegex, needCreole)) {
+        if (needCreole) {
+            goto creole;
+        }
 
-    if (trySequenceDiagram(kw, p_hints, p_isRegex)) {
         return kw;
     }
 
-    qDebug() << "tryCommonElements" << kw;
+    if (tryCommonElements(kw, p_hints, p_isRegex, needCreole)) {
+        if (needCreole) {
+            goto creole;
+        }
 
-    if (tryCommonElements(kw, p_hints, p_isRegex)) {
         return kw;
     }
 
+creole:
+    tryCreole(kw);
     return kw;
 }