LivePreview: smart live preview

This commit is contained in:
Le Tan 2018-08-24 20:16:33 +08:00
parent 13fb03bd11
commit cda48a612a
10 changed files with 193 additions and 23 deletions

View File

@ -190,7 +190,9 @@ new QWebChannel(qt.webChannelTransport,
content.requestPreviewEnabled.connect(setPreviewEnabled); content.requestPreviewEnabled.connect(setPreviewEnabled);
content.requestPreviewCodeBlock.connect(previewCodeBlock); content.requestPreviewCodeBlock.connect(previewCodeBlock);
content.requestSetPreviewContent.connect(setPreviewContent); content.requestSetPreviewContent.connect(setPreviewContent);
content.requestPerformSmartLivePreview.connect(performSmartLivePreview);
if (typeof updateHtml == "function") { if (typeof updateHtml == "function") {
updateHtml(content.html); updateHtml(content.html);
@ -1577,3 +1579,77 @@ var htmlToText = function(identifier, id, timeStamp, html) {
var markdown = ts.turndown(html); var markdown = ts.turndown(html);
content.htmlToTextCB(identifier, id, timeStamp, markdown); content.htmlToTextCB(identifier, id, timeStamp, markdown);
}; };
var performSmartLivePreview = function(lang, text) {
if (previewDiv.style.display == 'none') {
return;
}
if (lang != 'puml') {
return;
}
// PlantUML.
var targetNode = findNodeWithText(previewDiv, new RegExp(text));
if (!targetNode) {
return;
}
// (left, top) is relative to the viewport.
// Should add window.scrollX and window.scrollY to get the real content offset.
var trect = targetNode.getBoundingClientRect();
var vrect = {
left: document.documentElement.scrollLeft || document.body.scrollLeft || window.pageXOffset,
top: document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset,
width: document.documentElement.clientWidth || document.body.clientWidth,
height: document.documentElement.clientHeight || document.body.clientHeight
}
var dx = 0, dy = 0;
// If target is already in, do not scroll.
if (trect.left < 0
|| trect.left + trect.width > vrect.width) {
if (trect.width >= vrect.width) {
dx = trect.left;
} else {
dx = trect.left - (vrect.width - trect.width) / 2;
}
}
if (trect.top < 0
|| trect.top + trect.height > vrect.height) {
if (trect.height >= vrect.height) {
dy = trect.top;
} else {
dy = trect.top - (vrect.height - trect.width) / 2;
}
}
window.scrollBy(dx, dy);
}
var findNodeWithText = function(node, reg) {
var children = node.children;
if (children.length == 0) {
if (reg.test(node.textContent)) {
return node;
} else {
return null;
}
}
for (var i = 0; i < children.length; ++i) {
var ret = findNodeWithText(children[i], reg);
if (ret) {
return ret;
}
}
if (reg.test(node.textContent)) {
return node;
}
return null;
}

View File

@ -245,6 +245,9 @@ max_tag_label_length=10
; Max number of tag labels to display ; Max number of tag labels to display
max_num_of_tag_labels=3 max_num_of_tag_labels=3
; Smart live preview
smart_live_preview=true
[editor] [editor]
; Auto indent as previous line ; Auto indent as previous line
auto_indent=true auto_indent=true

View File

@ -311,6 +311,9 @@ void VConfigManager::initialize()
m_maxNumOfTagLabels = getConfigFromSettings("global", m_maxNumOfTagLabels = getConfigFromSettings("global",
"max_num_of_tag_labels").toInt(); "max_num_of_tag_labels").toInt();
m_smartLivePreview = getConfigFromSettings("global",
"smart_live_preview").toBool();
initEditorConfigs(); initEditorConfigs();
} }

View File

@ -547,6 +547,8 @@ public:
QChar getVimLeaderKey() const; QChar getVimLeaderKey() const;
bool getSmartLivePreview() const;
private: private:
// Look up a config from user and default settings. // Look up a config from user and default settings.
QVariant getConfigFromSettings(const QString &section, const QString &key) const; QVariant getConfigFromSettings(const QString &section, const QString &key) const;
@ -984,6 +986,9 @@ private:
// Vim leader key. // Vim leader key.
QChar m_vimLeaderKey; QChar m_vimLeaderKey;
// Smart live preview.
bool m_smartLivePreview;
// The name of the config file in each directory. // The name of the config file in each directory.
static const QString c_dirConfigFile; static const QString c_dirConfigFile;
@ -2539,4 +2544,9 @@ inline QChar VConfigManager::getVimLeaderKey() const
{ {
return m_vimLeaderKey; return m_vimLeaderKey;
} }
inline bool VConfigManager::getSmartLivePreview() const
{
return m_smartLivePreview;
}
#endif // VCONFIGMANAGER_H #endif // VCONFIGMANAGER_H

View File

@ -202,3 +202,10 @@ void VDocument::previewCodeBlockCB(int p_id, const QString &p_lang, const QStrin
{ {
emit codeBlockPreviewReady(p_id, p_lang, p_html); emit codeBlockPreviewReady(p_id, p_lang, p_html);
} }
void VDocument::performSmartLivePreview(const QString &p_lang, const QString &p_text)
{
if (!p_text.isEmpty()) {
emit requestPerformSmartLivePreview(p_lang, p_text);
}
}

View File

@ -75,6 +75,8 @@ public:
void muteWebView(bool p_muted); void muteWebView(bool p_muted);
void performSmartLivePreview(const QString &p_lang, const QString &p_text);
public slots: public slots:
// Will be called in the HTML side // Will be called in the HTML side
@ -192,6 +194,8 @@ signals:
void requestMuted(bool p_muted); void requestMuted(bool p_muted);
void requestPerformSmartLivePreview(const QString &p_lang, const QString &p_text);
private: private:
QString m_toc; QString m_toc;
QString m_header; QString m_header;

View File

@ -95,7 +95,7 @@ VLivePreviewHelper::VLivePreviewHelper(VEditor *p_editor,
{ {
m_livePreviewTimer = new QTimer(this); m_livePreviewTimer = new QTimer(this);
m_livePreviewTimer->setSingleShot(true); m_livePreviewTimer->setSingleShot(true);
m_livePreviewTimer->setInterval(100); m_livePreviewTimer->setInterval(500);
connect(m_livePreviewTimer, &QTimer::timeout, connect(m_livePreviewTimer, &QTimer::timeout,
this, &VLivePreviewHelper::handleCursorPositionChanged); this, &VLivePreviewHelper::handleCursorPositionChanged);
@ -214,6 +214,7 @@ void VLivePreviewHelper::updateCodeBlocks(TimeStamp p_timeStamp, const QVector<V
if (needUpdate) { if (needUpdate) {
updateLivePreview(); updateLivePreview();
performSmartLivePreview();
} }
clearObsoleteCache(); clearObsoleteCache();
@ -227,33 +228,33 @@ void VLivePreviewHelper::handleCursorPositionChanged()
} }
int cursorBlock = m_editor->textCursorW().block().blockNumber(); int cursorBlock = m_editor->textCursorW().block().blockNumber();
if (m_lastCursorBlock == cursorBlock) { if (m_lastCursorBlock != cursorBlock) {
return; m_lastCursorBlock = cursorBlock;
}
m_lastCursorBlock = cursorBlock; int left = 0, right = m_codeBlocks.size() - 1;
int mid = left;
while (left <= right) {
mid = (left + right) / 2;
const CodeBlockPreviewInfo &cb = m_codeBlocks[mid];
const VCodeBlock &vcb = cb.codeBlock();
if (vcb.m_startBlock <= cursorBlock && vcb.m_endBlock >= cursorBlock) {
break;
} else if (vcb.m_startBlock > cursorBlock) {
right = mid - 1;
} else {
left = mid + 1;
}
}
int left = 0, right = m_codeBlocks.size() - 1; if (left <= right) {
int mid = left; if (m_cbIndex != mid) {
while (left <= right) { m_cbIndex = mid;
mid = (left + right) / 2; updateLivePreview();
const CodeBlockPreviewInfo &cb = m_codeBlocks[mid]; }
const VCodeBlock &vcb = cb.codeBlock();
if (vcb.m_startBlock <= cursorBlock && vcb.m_endBlock >= cursorBlock) {
break;
} else if (vcb.m_startBlock > cursorBlock) {
right = mid - 1;
} else {
left = mid + 1;
} }
} }
if (left <= right) { performSmartLivePreview();
if (m_cbIndex != mid) {
m_cbIndex = mid;
updateLivePreview();
}
}
} }
void VLivePreviewHelper::updateLivePreview() void VLivePreviewHelper::updateLivePreview()
@ -396,6 +397,7 @@ void VLivePreviewHelper::localAsyncResultReady(int p_id,
} }
m_document->setPreviewContent(lang, p_result); m_document->setPreviewContent(lang, p_result);
performSmartLivePreview();
} else { } else {
// Inplace preview. // Inplace preview.
updateInplacePreview(); updateInplacePreview();
@ -523,3 +525,27 @@ void VLivePreviewHelper::clearObsoleteCache()
} }
} }
} }
void VLivePreviewHelper::performSmartLivePreview()
{
if (m_cbIndex < 0
|| m_cbIndex >= m_codeBlocks.size()
|| !g_config->getSmartLivePreview()) {
return;
}
const CodeBlockPreviewInfo &cb = m_codeBlocks[m_cbIndex];
const VCodeBlock &vcb = cb.codeBlock();
const QTextBlock block = m_editor->textCursorW().block();
if (block.blockNumber() <= vcb.m_startBlock
|| block.blockNumber() >= vcb.m_endBlock) {
return;
}
QString keyword;
if (vcb.m_lang == "puml" && m_plantUMLMode == PlantUMLMode::LocalPlantUML) {
keyword = VPlantUMLHelper::keywordForSmartLivePreview(block.text());
}
m_document->performSmartLivePreview(vcb.m_lang, keyword);
}

View File

@ -233,6 +233,8 @@ private:
void clearObsoleteCache(); void clearObsoleteCache();
void performSmartLivePreview();
// Sorted by m_startBlock in ascending order. // Sorted by m_startBlock in ascending order.
QVector<CodeBlockPreviewInfo> m_codeBlocks; QVector<CodeBlockPreviewInfo> m_codeBlocks;

View File

@ -188,3 +188,40 @@ QByteArray VPlantUMLHelper::process(const QString &p_format, const QString &p_te
return out; return out;
} }
static bool tryClassDiagram(QString &p_keyword)
{
{
// class ABC #Pink {
QRegExp classDef1("class\\s*(\\w+)\\s*.*");
if (classDef1.indexIn(p_keyword) >= 0) {
p_keyword = classDef1.cap(1);
return true;
}
}
{
// class "ABC DEF" as AD #Pink {
QRegExp classDef2("class\\s*\"([^\"]+)\"\\s*.*");
if (classDef2.indexIn(p_keyword) >= 0) {
p_keyword = classDef2.cap(1);
return true;
}
}
return false;
}
QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text)
{
QString kw = p_text.trimmed();
if (kw.isEmpty()) {
return kw;
}
if (tryClassDiagram(kw)) {
return kw;
}
return kw;
}

View File

@ -23,6 +23,8 @@ public:
static QByteArray process(const QString &p_format, const QString &p_text); static QByteArray process(const QString &p_format, const QString &p_text);
static QString keywordForSmartLivePreview(const QString &p_text);
signals: signals:
void resultReady(int p_id, void resultReady(int p_id,
TimeStamp p_timeStamp, TimeStamp p_timeStamp,