mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
LivePreview: refine smart live preview
- Support class diagram. - Support activity diagram.
This commit is contained in:
parent
28d5954bc3
commit
126600dbb1
@ -1622,7 +1622,7 @@ var htmlToText = function(identifier, id, timeStamp, html) {
|
||||
content.htmlToTextCB(identifier, id, timeStamp, markdown);
|
||||
};
|
||||
|
||||
var performSmartLivePreview = function(lang, text) {
|
||||
var performSmartLivePreview = function(lang, text, hints, isRegex) {
|
||||
if (previewDiv.style.display == 'none') {
|
||||
return;
|
||||
}
|
||||
@ -1632,7 +1632,35 @@ var performSmartLivePreview = function(lang, text) {
|
||||
}
|
||||
|
||||
// PlantUML.
|
||||
var targetNode = findNodeWithText(previewDiv, new RegExp(text));
|
||||
var targetNode = null;
|
||||
if (hints.indexOf('id') >= 0) {
|
||||
// isRegex is ignored.
|
||||
targetNode = findNodeWithText(previewDiv,
|
||||
text,
|
||||
function (node, text) {
|
||||
if (!node.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return node.id == text;
|
||||
});
|
||||
} else {
|
||||
if (isRegex) {
|
||||
var nodeReg = new RegExp(text);
|
||||
targetNode = findNodeWithText(previewDiv,
|
||||
text,
|
||||
function(node, text) {
|
||||
return nodeReg.test(node.textContent);
|
||||
});
|
||||
} else {
|
||||
targetNode = findNodeWithText(previewDiv,
|
||||
text,
|
||||
function(node, text) {
|
||||
return node.textContent.indexOf(text) >= 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetNode) {
|
||||
return;
|
||||
}
|
||||
@ -1665,17 +1693,17 @@ var performSmartLivePreview = function(lang, text) {
|
||||
if (trect.height >= vrect.height) {
|
||||
dy = trect.top;
|
||||
} else {
|
||||
dy = trect.top - (vrect.height - trect.width) / 2;
|
||||
dy = trect.top - (vrect.height - trect.height) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
window.scrollBy(dx, dy);
|
||||
}
|
||||
|
||||
var findNodeWithText = function(node, reg) {
|
||||
var findNodeWithText = function(node, text, isMatched) {
|
||||
var children = node.children;
|
||||
if (children.length == 0) {
|
||||
if (reg.test(node.textContent)) {
|
||||
if (isMatched(node, text)) {
|
||||
return node;
|
||||
} else {
|
||||
return null;
|
||||
@ -1683,13 +1711,13 @@ var findNodeWithText = function(node, reg) {
|
||||
}
|
||||
|
||||
for (var i = 0; i < children.length; ++i) {
|
||||
var ret = findNodeWithText(children[i], reg);
|
||||
var ret = findNodeWithText(children[i], text, isMatched);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (reg.test(node.textContent)) {
|
||||
if (isMatched(node, text)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -203,9 +203,13 @@ void VDocument::previewCodeBlockCB(int p_id, const QString &p_lang, const QStrin
|
||||
emit codeBlockPreviewReady(p_id, p_lang, p_html);
|
||||
}
|
||||
|
||||
void VDocument::performSmartLivePreview(const QString &p_lang, const QString &p_text)
|
||||
void VDocument::performSmartLivePreview(const QString &p_lang,
|
||||
const QString &p_text,
|
||||
const QString &p_hints,
|
||||
bool p_isRegex)
|
||||
{
|
||||
if (!p_text.isEmpty()) {
|
||||
emit requestPerformSmartLivePreview(p_lang, p_text);
|
||||
qDebug() << "performSmartLivePreview" << p_lang << p_text << p_hints << p_isRegex;
|
||||
emit requestPerformSmartLivePreview(p_lang, p_text, p_hints, p_isRegex);
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,10 @@ public:
|
||||
|
||||
void muteWebView(bool p_muted);
|
||||
|
||||
void performSmartLivePreview(const QString &p_lang, const QString &p_text);
|
||||
void performSmartLivePreview(const QString &p_lang,
|
||||
const QString &p_text,
|
||||
const QString &p_hints,
|
||||
bool p_isRegex);
|
||||
|
||||
public slots:
|
||||
// Will be called in the HTML side
|
||||
@ -194,7 +197,10 @@ signals:
|
||||
|
||||
void requestMuted(bool p_muted);
|
||||
|
||||
void requestPerformSmartLivePreview(const QString &p_lang, const QString &p_text);
|
||||
void requestPerformSmartLivePreview(const QString &p_lang,
|
||||
const QString &p_text,
|
||||
const QString &p_hints,
|
||||
bool p_isRegex);
|
||||
|
||||
private:
|
||||
QString m_toc;
|
||||
|
@ -535,6 +535,10 @@ void VLivePreviewHelper::performSmartLivePreview()
|
||||
}
|
||||
|
||||
const CodeBlockPreviewInfo &cb = m_codeBlocks[m_cbIndex];
|
||||
if (!cb.hasImageData()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const VCodeBlock &vcb = cb.codeBlock();
|
||||
const QTextBlock block = m_editor->textCursorW().block();
|
||||
if (block.blockNumber() <= vcb.m_startBlock
|
||||
@ -542,10 +546,13 @@ void VLivePreviewHelper::performSmartLivePreview()
|
||||
return;
|
||||
}
|
||||
|
||||
QString keyword;
|
||||
QString keyword, hints;
|
||||
bool isRegex = false;
|
||||
if (vcb.m_lang == "puml" && m_plantUMLMode == PlantUMLMode::LocalPlantUML) {
|
||||
keyword = VPlantUMLHelper::keywordForSmartLivePreview(block.text());
|
||||
keyword = VPlantUMLHelper::keywordForSmartLivePreview(block.text(),
|
||||
hints,
|
||||
isRegex);
|
||||
}
|
||||
|
||||
m_document->performSmartLivePreview(vcb.m_lang, keyword);
|
||||
m_document->performSmartLivePreview(vcb.m_lang, keyword, hints, isRegex);
|
||||
}
|
||||
|
@ -189,37 +189,269 @@ QByteArray VPlantUMLHelper::process(const QString &p_format, const QString &p_te
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool tryClassDiagram(QString &p_keyword)
|
||||
static bool tryClassDiagram(QString &p_keyword, QString &p_hints, bool &p_isRegex)
|
||||
{
|
||||
{
|
||||
Q_UNUSED(p_isRegex);
|
||||
|
||||
// class ABC #Pink {
|
||||
QRegExp classDef1("class\\s*(\\w+)\\s*.*");
|
||||
static QRegExp classDef1("^\\s*(?:class|(?:abstract(?:\\s+class)?)|interface|annotation|enum)\\s*"
|
||||
"(?!class)(\\w+)\\s*.*");
|
||||
if (classDef1.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = classDef1.cap(1);
|
||||
p_hints = "id";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// class "ABC DEF" as AD #Pink {
|
||||
QRegExp classDef2("class\\s*\"([^\"]+)\"\\s*.*");
|
||||
static QRegExp classDef2("^\\s*(?:class|(?:abstract(?:\\s+class)?)|interface|annotation|enum)\\s*"
|
||||
"\"([^\"]+)\"\\s*(?:\\bas (\\w+))?.*");
|
||||
if (classDef2.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = classDef2.cap(1);
|
||||
if (classDef2.cap(2).isEmpty()) {
|
||||
p_keyword = classDef2.cap(1);
|
||||
} else {
|
||||
p_keyword = classDef2.cap(2);
|
||||
}
|
||||
p_hints = "id";
|
||||
return true;
|
||||
}
|
||||
|
||||
// class01 "1" *-- "many" class02 : contains 4 >
|
||||
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()) {
|
||||
QString class2 = relation.cap(3);
|
||||
if (class2.isEmpty()) {
|
||||
class2 = relation.cap(4);
|
||||
}
|
||||
|
||||
p_keyword = class2;
|
||||
p_hints = "id";
|
||||
} else {
|
||||
p_keyword = note.trimmed();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// {static} field : String
|
||||
bool containsModifier = false;
|
||||
static QRegExp modifier("\\{(?:static|abstract|classifier)\\}");
|
||||
if (modifier.indexIn(p_keyword) >= 0) {
|
||||
containsModifier = true;
|
||||
p_keyword.remove(modifier);
|
||||
}
|
||||
|
||||
// + field
|
||||
static QRegExp member("^\\s*[-#~+]\\s*(.*)");
|
||||
if (member.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = member.cap(1).trimmed();
|
||||
return true;
|
||||
} else if (containsModifier) {
|
||||
p_keyword = p_keyword.trimmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
// note left on link: message
|
||||
// note left on link
|
||||
// node on link: message
|
||||
// MUST before next rule "note".
|
||||
static QRegExp note4("^\\s*note\\s+(?:(?:left|top|right|bottom)\\s+)?"
|
||||
"on\\s+link"
|
||||
"[^:]*"
|
||||
"(?::(.*))?");
|
||||
if (note4.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = note4.cap(1).trimmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
// note top of Object: message
|
||||
// note top of Object
|
||||
// note top: message
|
||||
static QRegExp note("^\\s*note\\s+(?:left|top|right|bottom)"
|
||||
"(?:\\s+of\\s+(\\w+))?\\s*"
|
||||
"(?::(.*))?");
|
||||
if (note.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = note.cap(2).trimmed();
|
||||
if (p_keyword.isEmpty()) {
|
||||
p_keyword = note.cap(1);
|
||||
if (!p_keyword.isEmpty()) {
|
||||
p_hints = "id";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// note "a floating note" as N1
|
||||
// note as N1
|
||||
static QRegExp note2("^\\s*note\\s+(?:\"([^\"]*)\"\\s+)?as\\s+\\w+\\s*");
|
||||
if (note2.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = note2.cap(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// end note
|
||||
static QRegExp note3("^\\s*end note\\s*$");
|
||||
if (note3.indexIn(p_keyword) >= 0) {
|
||||
p_keyword.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text)
|
||||
static bool tryCommonElements(QString &p_keyword, QString &p_hints, bool &p_isRegex)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Words in quotes.
|
||||
// cmf("abc")
|
||||
static QRegExp quote("^[^\"]*\"([^\"]+)\"[^\"]*$");
|
||||
if (quote.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = quote.cap(1).trimmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool tryActivityDiagram(QString &p_keyword, QString &p_hints, bool &p_isRegex)
|
||||
{
|
||||
Q_UNUSED(p_isRegex);
|
||||
Q_UNUSED(p_hints);
|
||||
|
||||
// Activity. (Do not support color.)
|
||||
// :Hello world;
|
||||
// :Across multiple lines
|
||||
static QRegExp activity1("^\\s*:(.+)\\s*$");
|
||||
if (activity1.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = activity1.cap(1).trimmed();
|
||||
if (!p_keyword.isEmpty()) {
|
||||
QChar ch = p_keyword[p_keyword.size() - 1];
|
||||
if (ch == ';' || ch == '|' || ch == '<' || ch == '>'
|
||||
|| ch == '/' || ch == ']' || ch == '}') {
|
||||
p_keyword = p_keyword.left(p_keyword.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Activity.
|
||||
// multiple lines;
|
||||
static QRegExp activity2("^\\s*(.+)[;|<>/\\]}]\\s*$");
|
||||
if (activity2.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = activity2.cap(1).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) {
|
||||
p_keyword.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Conditionals.
|
||||
// if (Graphviz) then (yes)
|
||||
// else if (Graphviz) then (yes)
|
||||
static QRegExp conIf("^\\s*(?:else)?if\\s+\\(([^\\)]+)\\)\\s+then(?:\\s+\\([^\\)]+\\))?\\s*$");
|
||||
if (conIf.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = conIf.cap(1).trimmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
// else (no)
|
||||
static QRegExp conElse("^\\s*else(?:\\s+\\(([^\\)]+)\\))?\\s*$");
|
||||
if (conElse.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = conElse.cap(1).trimmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Repeat loop.
|
||||
// repeat while (more data?)
|
||||
static QRegExp repeat("^\\s*repeat\\s+while\\s+\\(([^\\)]+)\\)\\s*$");
|
||||
if (repeat.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = repeat.cap(1).trimmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
// while (check?) is (not empty)
|
||||
static QRegExp whileLoop("^\\s*while\\s+\\(([^\\)]+)\\)(?:\\s+is\\s+\\([^\\)]+\\))?\\s*$");
|
||||
if (whileLoop.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = whileLoop.cap(1).trimmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
// endwhile (empty)
|
||||
static QRegExp endWhile("^\\s*endwhile(?:\\s+\\(([^\\)]+)\\))?\\s*$");
|
||||
if (endWhile.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = endWhile.cap(1).trimmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
// partition Running {
|
||||
static QRegExp partition("^\\s*partition\\s+(\\w+)\\s+\\{\\s*$");
|
||||
if (partition.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = partition.cap(1).trimmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
// |Swimlane1|
|
||||
// |#Pink|Swimlane1|
|
||||
static QRegExp swimline("^\\s*(?:\\|[^\\|]+)?\\|([^|]+)\\|\\s*$");
|
||||
if (swimline.indexIn(p_keyword) >= 0) {
|
||||
p_keyword = swimline.cap(1).trimmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text,
|
||||
QString &p_hints,
|
||||
bool &p_isRegex)
|
||||
{
|
||||
QString kw = p_text.trimmed();
|
||||
if (kw.isEmpty()) {
|
||||
return kw;
|
||||
}
|
||||
|
||||
if (tryClassDiagram(kw)) {
|
||||
p_isRegex = false;
|
||||
|
||||
qDebug() << "tryClassDiagram" << kw;
|
||||
|
||||
if (tryClassDiagram(kw, p_hints, p_isRegex)) {
|
||||
return kw;
|
||||
}
|
||||
|
||||
qDebug() << "tryActivityDiagram" << kw;
|
||||
|
||||
if (tryActivityDiagram(kw, p_hints, p_isRegex)) {
|
||||
return kw;
|
||||
}
|
||||
|
||||
qDebug() << "tryCommonElements" << kw;
|
||||
|
||||
if (tryCommonElements(kw, p_hints, p_isRegex)) {
|
||||
return kw;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,9 @@ public:
|
||||
|
||||
static QByteArray process(const QString &p_format, const QString &p_text);
|
||||
|
||||
static QString keywordForSmartLivePreview(const QString &p_text);
|
||||
static QString keywordForSmartLivePreview(const QString &p_text,
|
||||
QString &p_hints,
|
||||
bool &p_isRegex);
|
||||
|
||||
signals:
|
||||
void resultReady(int p_id,
|
||||
|
Loading…
x
Reference in New Issue
Block a user