From 74ec3884d047ae8c63e08165992058b716ccbb88 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Thu, 30 Aug 2018 19:53:20 +0800 Subject: [PATCH] LivePreview: search for multiple tokens and select the best match --- src/resources/markdown_template.js | 105 +++++++++++++++++++++-------- src/vplantumlhelper.cpp | 75 +++++++++++++++------ 2 files changed, 131 insertions(+), 49 deletions(-) diff --git a/src/resources/markdown_template.js b/src/resources/markdown_template.js index 3d32b80c..b7fcc4a6 100644 --- a/src/resources/markdown_template.js +++ b/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; } + + return res.stop; } - if (isMatched(node, text)) { - return node; - } - - return null; + return false; } // Draw a rectangle to mark @rect. diff --git a/src/vplantumlhelper.cpp b/src/vplantumlhelper.cpp index c12a01fb..ffc5d0ad 100644 --- a/src/vplantumlhelper.cpp +++ b/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; }