diff --git a/src/resources/themes/v_detorte/v_detorte.mdhl b/src/resources/themes/v_detorte/v_detorte.mdhl index b53da587..eda218a1 100644 --- a/src/resources/themes/v_detorte/v_detorte.mdhl +++ b/src/resources/themes/v_detorte/v_detorte.mdhl @@ -146,83 +146,39 @@ font-family: YaHei Consolas Hybrid, Consolas, Monaco, Andale Mono, Monospace, Co # [VNote] Codeblock sylte from HighlightJS (bold, italic, underlined, strikeout, color) # The last occurence of the same attribute takes effect # Could specify multiple attribute in one line -hll: 404040 -c: 999999 -err: a61717 -esc: d0d0d0 -g: d0d0d0 -k: bold, 6ab825 -l: d0d0d0 -n: d0d0d0 -o: d0d0d0 -x: d0d0d0 -p: d0d0d0 -ch: italic, 999999 -cm: italic, 999999 -cp: bold, cd2828 -cpf: italic, 999999 -c1: italic, 999999 -cs: bold, e50808 -gd: d22323 -ge: italic, d0d0d0 -gr: d22323 -gh: bold, ffffff -gi: 589819 -go: cccccc -gp: aaaaaa -gs: bold, d0d0d0 -gu: underlined, ffffff -gt: d22323 -kc: bold, 6ab825 -kd: bold, 6ab825 -kn: bold, 6ab825 -kp: 6ab825 -kr: bold, 6ab825 -kt: bold, 6ab825 -ld: d0d0d0 -m: 3677a9 -s: ed9d13 -na: bbbbbb -nb: 24909d -nc: underlined, 447fcf -no: 40ffff -nd: ffa500 -ni: d0d0d0 -ne: bbbbbb -nf: 447fcf -nl: d0d0d0 -nn: underlined, 447fcf -nx: d0d0d0 -py: d0d0d0 -nt: bold, 6ab825 -nv: 40ffff -ow: bold, 6ab825 -w: 666666 -mb: 3677a9 -mf: 3677a9 -mh: 3677a9 -mi: 3677a9 -mo: 3677a9 -sa: ed9d13 -sb: ed9d13 -sc: ed9d13 -dl: ed9d13 -sd: ed9d13 -s2: ed9d13 -se: ed9d13 -sh: ed9d13 -si: ed9d13 -sx: ffa500 -sr: ed9d13 -s1: ed9d13 -ss: ed9d13 -bp: 24909d -fm: 447fcf -vc: 40ffff -vg: 40ffff -vi: 40ffff -vm: 40ffff -il: 3677a9 +hljs-comment: af8787 +hljs-quote: af8787 +hljs-doctag: ccb24c +hljs-keyword: ccb24c +hljs-formula: ccb24c +hljs-section: e37c84 +hljs-name: e37c84 +hljs-selector-tag: e37c84 +hljs-deletion: e37c84 +hljs-subst: e37c84 +hljs-literal: 56b6c2 +hljs-string: f06292 +hljs-regexp: f06292 +hljs-addition: f06292 +hljs-attribute: f06292 +hljs-meta-string: f06292 +hljs-built_in: 80cbc4 +hljs-attr: ce93db +hljs-variable: ce93db +hljs-template-variable: ce93db +hljs-type: ce93db +hljs-selector-class: ce93db +hljs-selector-attr: ce93db +hljs-selector-pseudo: ce93db +hljs-number: ce93db +hljs-symbol: 84c0f2 +hljs-link: underlined, 84c0f2 +hljs-bullet: 84c0f2 +hljs-meta: 84c0f2 +hljs-selector-id: 84c0f2 +hljs-title: bold, 84c0f2 +hljs-emphasis: italic +hljs-strong: bold BLOCKQUOTE foreground: ccb24c diff --git a/src/resources/themes/v_moonlight/v_moonlight.mdhl b/src/resources/themes/v_moonlight/v_moonlight.mdhl index 19347366..0a727634 100644 --- a/src/resources/themes/v_moonlight/v_moonlight.mdhl +++ b/src/resources/themes/v_moonlight/v_moonlight.mdhl @@ -144,83 +144,40 @@ font-family: YaHei Consolas Hybrid, Consolas, Monaco, Andale Mono, Monospace, Co # [VNote] Codeblock sylte from HighlightJS (bold, italic, underlined, strikeout, color) # The last occurence of the same attribute takes effect # Could specify multiple attribute in one line -hll: 404040 -c: 999999 -err: a61717 -esc: d0d0d0 -g: d0d0d0 -k: bold, 6ab825 -l: d0d0d0 -n: d0d0d0 -o: d0d0d0 -x: d0d0d0 -p: d0d0d0 -ch: italic, 999999 -cm: italic, 999999 -cp: bold, cd2828 -cpf: italic, 999999 -c1: italic, 999999 -cs: bold, e50808 -gd: d22323 -ge: italic, d0d0d0 -gr: d22323 -gh: bold, ffffff -gi: 589819 -go: cccccc -gp: aaaaaa -gs: bold, d0d0d0 -gu: underlined, ffffff -gt: d22323 -kc: bold, 6ab825 -kd: bold, 6ab825 -kn: bold, 6ab825 -kp: 6ab825 -kr: bold, 6ab825 -kt: bold, 6ab825 -ld: d0d0d0 -m: 3677a9 -s: ed9d13 -na: bbbbbb -nb: 24909d -nc: underlined, 447fcf -no: 40ffff -nd: ffa500 -ni: d0d0d0 -ne: bbbbbb -nf: 447fcf -nl: d0d0d0 -nn: underlined, 447fcf -nx: d0d0d0 -py: d0d0d0 -nt: bold, 6ab825 -nv: 40ffff -ow: bold, 6ab825 -w: 666666 -mb: 3677a9 -mf: 3677a9 -mh: 3677a9 -mi: 3677a9 -mo: 3677a9 -sa: ed9d13 -sb: ed9d13 -sc: ed9d13 -dl: ed9d13 -sd: ed9d13 -s2: ed9d13 -se: ed9d13 -sh: ed9d13 -si: ed9d13 -sx: ffa500 -sr: ed9d13 -s1: ed9d13 -ss: ed9d13 -bp: 24909d -fm: 447fcf -vc: 40ffff -vg: 40ffff -vi: 40ffff -vm: 40ffff -il: 3677a9 +hljs-comment: 6e7686 +hljs-quote: 6e7686 +hljs-doctag: c678dd +hljs-keyword: c678dd +hljs-formula: c678dd +hljs-section: e06c75 +hljs-name: e06c75 +hljs-selector-tag: e06c75 +hljs-deletion: e06c75 +hljs-subst: e06c75 +hljs-literal: 56b6c2 +hljs-string: 98c379 +hljs-regexp: 98c379 +hljs-addition: 98c379 +hljs-attribute: 98c379 +hljs-meta-string: 98c379 +hljs-built_in: e6c07b +hljs-class: e6c07b +hljs-attr: d19a66 +hljs-variable: d19a66 +hljs-template-variable: d19a66 +hljs-type: d19a66 +hljs-selector-class: d19a66 +hljs-selector-attr: d19a66 +hljs-selector-pseudo: d19a66 +hljs-number: d19a66 +hljs-symbol: 61aeee +hljs-link: underlined, 61aeee +hljs-bullet: 61aeee +hljs-meta: 61aeee +hljs-selector-id: 61aeee +hljs-title: bold, 61aeee +hljs-emphasis: italic +hljs-strong: bold BLOCKQUOTE foreground: 6e7686 diff --git a/src/resources/themes/v_native/v_native.mdhl b/src/resources/themes/v_native/v_native.mdhl index 95458b37..52bde867 100644 --- a/src/resources/themes/v_native/v_native.mdhl +++ b/src/resources/themes/v_native/v_native.mdhl @@ -138,69 +138,42 @@ font-family: YaHei Consolas Hybrid, Consolas, Monaco, Andale Mono, Monospace, Co FENCEDCODEBLOCK foreground: 673ab7 font-family: YaHei Consolas Hybrid, Consolas, Monaco, Andale Mono, Monospace, Courier New -# [VNote] Codeblock sylte from Pygment (bold, italic, underlined, strikeout, color) +# [VNote] Codeblock sylte from HighlightJS (bold, italic, underlined, strikeout, color) # The last occurence of the same attribute takes effect -hll: ffffcc -c: 888888 -err: FF0000 -k: 008800 -o: 333333 -ch: 888888 -cm: 888888 -cp: 557799 -cpf: 4d99bf -c1: 888888 -cs: cc0000 -gd: A00000 -ge: italic -gr: FF0000 -gh: 000080 -gi: 00A000 -go: 888888 -gp: c65d09 -gs: bold -gu: bold, 800080 -gt: bold, 0044DD -kc: bold, 008800 -kd: bold, 008800 -kn: bold, 008800 -kp: bold, 003388 -kr: bold, 008800 -kt: bold, 333399 -m: bold, 6600EE -s: af00d7 -na: 0000CC -nb: 007020 -nc: bold, BB0066 -no: bold, 003366 -nd: bold, 555555 -ni: bold, 880000 -ne: bold, FF0000 -nf: bold, 0066BB -nl: bold, 997700 -nn: bold, 0e84b5 -nt: 007700 -nv: 996633 -ow: bold, 000000 -w: bbbbbb -mb: bold, 6600EE -mf: bold, 6600EE -mh: bold, 005588 -mi: bold, 0000DD -mo: bold, 4400EE -sc: 0044DD -sd: DD4422 -se: 666666 -sx: DD2200 -sr: 000000 -ss: AA6600 -bp: 007020 -fm: bold, 0066BB -vc: 336699 -vg: bold, dd7700 -vi: 3333BB -vm: 996633 -il: bold, 0000DD +hljs-comment: 6c6c6c +hljs-keyword: 0000ee +hljs-attribute: 0000ee +hljs-selector-tag: 0000ee +hljs-meta-keyword: 0000ee +hljs-doctag: 0000ee +hljs-name: 0000ee +hljs-type: 880000 +hljs-string: 880000 +hljs-number: 880000 +hljs-selector-id: 880000 +hljs-selector-class: 880000 +hljs-quote: 880000 +hljs-template-tag: 880000 +hljs-deletion: 880000 +# Could specify multiple attribute in one line +hljs-title: bold, 880000 +hljs-section: bold, 880000 +hljs-regexp: bc6060 +hljs-symbol: bc6060 +hljs-variable: bc6060 +hljs-template-variable: bc6060 +hljs-link: bc6060 +hljs-selector-attr: bc6060 +hljs-selector-pseudo: bc6060 +hljs-literal: af00d7 +hljs-built_in: 008700 +hljs-bullet: 008700 +hljs-code: 008700 +hljs-addition: 008700 +hljs-meta: 1f7199 +hljs-meta-string: 4d99bf +hljs-emphasis: italic +hljs-strong: bold BLOCKQUOTE foreground: 00af00 diff --git a/src/resources/themes/v_pure/v_pure.mdhl b/src/resources/themes/v_pure/v_pure.mdhl index abc3ebf4..6a3fe4b9 100644 --- a/src/resources/themes/v_pure/v_pure.mdhl +++ b/src/resources/themes/v_pure/v_pure.mdhl @@ -139,70 +139,42 @@ font-family: YaHei Consolas Hybrid, Consolas, Monaco, Andale Mono, Monospace, Co FENCEDCODEBLOCK foreground: 673ab7 font-family: YaHei Consolas Hybrid, Consolas, Monaco, Andale Mono, Monospace, Courier New -# [VNote] Codeblock sylte from Pygment (bold, italic, underlined, strikeout, color) +# [VNote] Codeblock sylte from HighlightJS (bold, italic, underlined, strikeout, color) # The last occurence of the same attribute takes effect # Could specify multiple attribute in one line -hll: ffffcc -c: 888888 -err: FF0000 -k: 008800 -o: 333333 -ch: 888888 -cm: 888888 -cp: 557799 -cpf: 4d99bf -c1: 888888 -cs: cc0000 -gd: A00000 -ge: italic -gr: FF0000 -gh: 000080 -gi: 00A000 -go: 888888 -gp: c65d09 -gs: bold -gu: bold, 800080 -gt: bold, 0044DD -kc: bold, 008800 -kd: bold, 008800 -kn: bold, 008800 -kp: bold, 003388 -kr: bold, 008800 -kt: bold, 333399 -m: bold, 6600EE -s: af00d7 -na: 0000CC -nb: 007020 -nc: bold, BB0066 -no: bold, 003366 -nd: bold, 555555 -ni: bold, 880000 -ne: bold, FF0000 -nf: bold, 0066BB -nl: bold, 997700 -nn: bold, 0e84b5 -nt: 007700 -nv: 996633 -ow: bold, 000000 -w: bbbbbb -mb: bold, 6600EE -mf: bold, 6600EE -mh: bold, 005588 -mi: bold, 0000DD -mo: bold, 4400EE -sc: 0044DD -sd: DD4422 -se: 666666 -sx: DD2200 -sr: 000000 -ss: AA6600 -bp: 007020 -fm: bold, 0066BB -vc: 336699 -vg: bold, dd7700 -vi: 3333BB -vm: 996633 -il: bold, 0000DD +hljs-comment: 6c6c6c +hljs-keyword: 0000ee +hljs-attribute: 0000ee +hljs-selector-tag: 0000ee +hljs-meta-keyword: 0000ee +hljs-doctag: 0000ee +hljs-name: 0000ee +hljs-type: 880000 +hljs-string: 880000 +hljs-number: 880000 +hljs-selector-id: 880000 +hljs-selector-class: 880000 +hljs-quote: 880000 +hljs-template-tag: 880000 +hljs-deletion: 880000 +hljs-title: bold, 880000 +hljs-section: bold, 880000 +hljs-regexp: bc6060 +hljs-symbol: bc6060 +hljs-variable: bc6060 +hljs-template-variable: bc6060 +hljs-link: bc6060 +hljs-selector-attr: bc6060 +hljs-selector-pseudo: bc6060 +hljs-literal: af00d7 +hljs-built_in: 008700 +hljs-bullet: 008700 +hljs-code: 008700 +hljs-addition: 008700 +hljs-meta: 1f7199 +hljs-meta-string: 4d99bf +hljs-emphasis: italic +hljs-strong: bold BLOCKQUOTE foreground: 00af00 diff --git a/src/resources/themes/v_simple/v_simple.mdhl b/src/resources/themes/v_simple/v_simple.mdhl index 0e863ff8..04b4b7a2 100644 --- a/src/resources/themes/v_simple/v_simple.mdhl +++ b/src/resources/themes/v_simple/v_simple.mdhl @@ -135,67 +135,39 @@ font-family: YaHei Consolas Hybrid, Consolas, Monaco, Andale Mono, Monospace, Co # [VNote] Codeblock sylte from HighlightJS (bold, italic, underlined, strikeout, color) # The last occurence of the same attribute takes effect # Could specify multiple attribute in one line -hll: ffffcc -c: 888888 -err: FF0000 -k: 008800 -o: 333333 -ch: 888888 -cm: 888888 -cp: 557799 -cpf: 4d99bf -c1: 888888 -cs: cc0000 -gd: A00000 -ge: italic -gr: FF0000 -gh: 000080 -gi: 00A000 -go: 888888 -gp: c65d09 -gs: bold -gu: bold, 800080 -gt: bold, 0044DD -kc: bold, 008800 -kd: bold, 008800 -kn: bold, 008800 -kp: bold, 003388 -kr: bold, 008800 -kt: bold, 333399 -m: bold, 6600EE -s: af00d7 -na: 0000CC -nb: 007020 -nc: bold, BB0066 -no: bold, 003366 -nd: bold, 555555 -ni: bold, 880000 -ne: bold, FF0000 -nf: bold, 0066BB -nl: bold, 997700 -nn: bold, 0e84b5 -nt: 007700 -nv: 996633 -ow: bold, 000000 -w: bbbbbb -mb: bold, 6600EE -mf: bold, 6600EE -mh: bold, 005588 -mi: bold, 0000DD -mo: bold, 4400EE -sc: 0044DD -sd: DD4422 -se: 666666 -sx: DD2200 -sr: 000000 -ss: AA6600 -bp: 007020 -fm: bold, 0066BB -vc: 336699 -vg: bold, dd7700 -vi: 3333BB -vm: 996633 -il: bold, 0000DD +hljs-comment: 6c6c6c +hljs-keyword: 0000ee +hljs-attribute: 0000ee +hljs-selector-tag: 0000ee +hljs-meta-keyword: 0000ee +hljs-doctag: 0000ee +hljs-name: 0000ee +hljs-type: 880000 +hljs-string: 880000 +hljs-number: 880000 +hljs-selector-id: 880000 +hljs-selector-class: 880000 +hljs-quote: 880000 +hljs-template-tag: 880000 +hljs-deletion: 880000 +hljs-title: bold, 880000 +hljs-section: bold, 880000 +hljs-regexp: bc6060 +hljs-symbol: bc6060 +hljs-variable: bc6060 +hljs-template-variable: bc6060 +hljs-link: bc6060 +hljs-selector-attr: bc6060 +hljs-selector-pseudo: bc6060 +hljs-literal: af00d7 +hljs-built_in: 008700 +hljs-bullet: 008700 +hljs-code: 008700 +hljs-addition: 008700 +hljs-meta: 1f7199 +hljs-meta-string: 4d99bf +hljs-emphasis: italic +hljs-strong: bold BLOCKQUOTE foreground: 00af00 diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index 7d91b481..7fda65e1 100644 --- a/src/resources/vnote.ini +++ b/src/resources/vnote.ini @@ -424,7 +424,7 @@ plantuml_cmd= graphviz_dot= ; Whether enable copy button in code block -enable_code_block_copy_button=true +enable_code_block_copy_button=false [shortcuts] ; Define shortcuts here, with each item in the form "operation=keysequence". diff --git a/src/src.pro b/src/src.pro index 43be1e77..10430d53 100644 --- a/src/src.pro +++ b/src/src.pro @@ -68,6 +68,7 @@ SOURCES += main.cpp\ vnavigationmode.cpp \ vorphanfile.cpp \ vcodeblockhighlighthelper.cpp \ + vcodeblockhighlighthelper2.cpp \ vmdtab.cpp \ vhtmltab.cpp \ utils/vvim.cpp \ @@ -202,6 +203,7 @@ HEADERS += vmainwindow.h \ vnavigationmode.h \ vorphanfile.h \ vcodeblockhighlighthelper.h \ + vcodeblockhighlighthelper2.h \ vmdtab.h \ vhtmltab.h \ utils/vvim.h \ diff --git a/src/vcodeblockhighlighthelper2.cpp b/src/vcodeblockhighlighthelper2.cpp new file mode 100644 index 00000000..99cdd055 --- /dev/null +++ b/src/vcodeblockhighlighthelper2.cpp @@ -0,0 +1,307 @@ +#include "vcodeblockhighlighthelper2.h" + +#include +#include + +#include "vdocument.h" +#include "utils/vutils.h" +#include "pegmarkdownhighlighter.h" + +VCodeBlockHighlightHelper2::VCodeBlockHighlightHelper2(PegMarkdownHighlighter *p_highlighter, + VDocument *p_vdoc, + MarkdownConverterType p_type) + : QObject(p_highlighter), + m_highlighter(p_highlighter), + m_vdocument(p_vdoc), + m_type(p_type), + m_timeStamp(0) +{ + connect(m_highlighter, &PegMarkdownHighlighter::codeBlocksUpdated, + this, &VCodeBlockHighlightHelper2::handleCodeBlocksUpdated); + connect(m_vdocument, &VDocument::textHighlighted, + this, &VCodeBlockHighlightHelper2::handleTextHighlightResult); + + // Web side is ready for code block highlight. + connect(m_vdocument, &VDocument::readyToHighlightText, + m_highlighter, &PegMarkdownHighlighter::updateHighlight); +} + +QString VCodeBlockHighlightHelper2::unindentCodeBlock(const QString &p_text) +{ + if (p_text.isEmpty()) { + return p_text; + } + + QStringList lines = p_text.split('\n'); + Q_ASSERT(lines[0].trimmed().startsWith("```") || lines[0].trimmed().startsWith("~~~")); + Q_ASSERT(lines.size() > 1); + + QRegExp regExp("(^\\s*)"); + regExp.indexIn(lines[0]); + V_ASSERT(regExp.captureCount() == 1); + int nrSpaces = regExp.capturedTexts()[1].size(); + + if (nrSpaces == 0) { + return p_text; + } + + QString res = lines[0].right(lines[0].size() - nrSpaces); + for (int i = 1; i < lines.size(); ++i) { + const QString &line = lines[i]; + + int idx = 0; + while (idx < nrSpaces && idx < line.size() && line[idx].isSpace()) { + ++idx; + } + res = res + "\n" + line.right(line.size() - idx); + } + + return res; +} + +void VCodeBlockHighlightHelper2::handleCodeBlocksUpdated(TimeStamp p_timeStamp, + const QVector &p_codeBlocks) +{ + if (!m_vdocument->isReadyToHighlight()) { + // Immediately return empty results. + QVector emptyRes; + for (int i = 0; i < p_codeBlocks.size(); ++i) { + updateHighlightResults(p_timeStamp, 0, emptyRes); + } + + return; + } + + m_timeStamp = p_timeStamp; + m_codeBlocks = p_codeBlocks; + for (int i = 0; i < m_codeBlocks.size(); ++i) { + const VCodeBlock &block = m_codeBlocks[i]; + auto it = m_cache.find(block.m_text); + if (it != m_cache.end()) { + // Hit cache. + qDebug() << "code block highlight hit cache" << p_timeStamp << i; + it.value().m_timeStamp = p_timeStamp; + updateHighlightResults(p_timeStamp, block.m_startPos, it.value().m_units); + } else { + QString unindentedText = unindentCodeBlock(block.m_text); + m_vdocument->highlightTextAsync(unindentedText, i, p_timeStamp); + } + } +} + +void VCodeBlockHighlightHelper2::handleTextHighlightResult(const QString &p_html, + int p_id, + unsigned long long p_timeStamp) +{ + // Abandon obsolete result. + if (m_timeStamp != p_timeStamp) { + return; + } + + parseHighlightResult(p_timeStamp, p_id, p_html); +} + +static void revertEscapedHtml(QString &p_html) +{ + p_html.replace(">", ">").replace("<", "<").replace("&", "&"); +} + +// Search @p_tokenStr in @p_text from p_index. Spaces after `\n` will not make +// a difference in the match. The matched range will be returned as +// [@p_start, @p_end]. Update @p_index to @p_end + 1. +// Set @p_start and @p_end to -1 to indicate mismatch. +static void matchTokenRelaxed(const QString &p_text, const QString &p_tokenStr, + int &p_index, int &p_start, int &p_end) +{ + QString regStr = QRegExp::escape(p_tokenStr); + + // Remove the leading spaces. + int nonSpaceIdx = 0; + while (nonSpaceIdx < regStr.size() && regStr[nonSpaceIdx].isSpace()) { + ++nonSpaceIdx; + } + + if (nonSpaceIdx > 0 && nonSpaceIdx < regStr.size()) { + regStr.remove(0, nonSpaceIdx); + } + + // Do not replace the ending '\n'. + regStr.replace(QRegExp("\n(?!$)"), "\\s+"); + + QRegExp regExp(regStr); + p_start = p_text.indexOf(regExp, p_index); + if (p_start == -1) { + p_end = -1; + return; + } + + p_end = p_start + regExp.matchedLength() - 1; + p_index = p_end + 1; +} + +// For now, we could only handle code blocks outside the list. +void VCodeBlockHighlightHelper2::parseHighlightResult(TimeStamp p_timeStamp, + int p_idx, + const QString &p_html) +{ + const VCodeBlock &block = m_codeBlocks.at(p_idx); + int startPos = block.m_startPos; + QString text = block.m_text; + + QVector hlUnits; + + bool failed = true; + + QXmlStreamReader xml(p_html); + + // Must have a fenced line at the front. + // textIndex is the start index in the code block text to search for. + int textIndex = text.indexOf('\n'); + if (textIndex == -1) { + goto exit; + } + ++textIndex; + + if (xml.readNextStartElement()) { + if (xml.name() != "pre") { + goto exit; + } + + if (!xml.readNextStartElement()) { + goto exit; + } + + if (xml.name() != "code") { + goto exit; + } + + while (xml.readNext()) { + if (xml.isCharacters()) { + // Revert the HTML escape to match. + QString tokenStr = xml.text().toString(); + revertEscapedHtml(tokenStr); + + int start, end; + matchTokenRelaxed(text, tokenStr, textIndex, start, end); + if (start == -1) { + failed = true; + goto exit; + } + } else if (xml.isStartElement()) { + if (xml.name() != "span") { + failed = true; + goto exit; + } + if (!parseSpanElement(xml, text, textIndex, hlUnits)) { + failed = true; + goto exit; + } + } else if (xml.isEndElement()) { + if (xml.name() != "code" && xml.name() != "pre") { + failed = true; + } else { + failed = false; + } + goto exit; + } else { + failed = true; + goto exit; + } + } + } + +exit: + // Pass result back to highlighter. + // Abandon obsolete result. + if (m_timeStamp != p_timeStamp) { + return; + } + + if (xml.hasError() || failed) { + qWarning() << "fail to parse highlighted result" + << "stamp:" << p_timeStamp << "index:" << p_idx << p_html; + hlUnits.clear(); + } + + // Add it to cache. + addToHighlightCache(text, p_timeStamp, hlUnits); + + updateHighlightResults(p_timeStamp, startPos, hlUnits); +} + +void VCodeBlockHighlightHelper2::updateHighlightResults(TimeStamp p_timeStamp, + int p_startPos, + QVector p_units) +{ + for (int i = 0; i < p_units.size(); ++i) { + p_units[i].m_position += p_startPos; + } + + // We need to call this function anyway to trigger the rehighlight. + m_highlighter->setCodeBlockHighlights(p_timeStamp, p_units); +} + +bool VCodeBlockHighlightHelper2::parseSpanElement(QXmlStreamReader &p_xml, + const QString &p_text, + int &p_index, + QVector &p_units) +{ + int unitStart = p_index; + QString style = p_xml.attributes().value("class").toString(); + + while (p_xml.readNext()) { + if (p_xml.isCharacters()) { + // Revert the HTML escape to match. + QString tokenStr = p_xml.text().toString(); + revertEscapedHtml(tokenStr); + + int start, end; + matchTokenRelaxed(p_text, tokenStr, p_index, start, end); + if (start == -1) { + return false; + } + } else if (p_xml.isStartElement()) { + if (p_xml.name() != "span") { + return false; + } + + // Sub-span. + if (!parseSpanElement(p_xml, p_text, p_index, p_units)) { + return false; + } + } else if (p_xml.isEndElement()) { + if (p_xml.name() != "span") { + return false; + } + + // Got a complete span. Use relative position here. + HLUnitPos unit(unitStart, p_index - unitStart, style); + p_units.append(unit); + return true; + } else { + return false; + } + } + return false; +} + +void VCodeBlockHighlightHelper2::addToHighlightCache(const QString &p_text, + TimeStamp p_timeStamp, + const QVector &p_units) +{ + const int c_maxEntries = 100; + const TimeStamp c_maxTimeStampSpan = 3; + if (m_cache.size() >= c_maxEntries) { + // Remove the oldest one. + TimeStamp ts = p_timeStamp - c_maxTimeStampSpan; + for (auto it = m_cache.begin(); it != m_cache.end();) { + if (it.value().m_timeStamp < ts) { + it = m_cache.erase(it); + } else { + ++it; + } + } + } + + m_cache.insert(p_text, HLResult(p_timeStamp, p_units)); +} diff --git a/src/vcodeblockhighlighthelper2.h b/src/vcodeblockhighlighthelper2.h new file mode 100644 index 00000000..050ae9d4 --- /dev/null +++ b/src/vcodeblockhighlighthelper2.h @@ -0,0 +1,81 @@ +#ifndef VCODEBLOCKHIGHLIGHTHELPER2_H +#define VCODEBLOCKHIGHLIGHTHELPER2_H + +#include +#include +#include +#include +#include + +#include "vconfigmanager.h" + +class VDocument; +class PegMarkdownHighlighter; + +class VCodeBlockHighlightHelper2 : public QObject +{ + Q_OBJECT +public: + VCodeBlockHighlightHelper2(PegMarkdownHighlighter *p_highlighter, + VDocument *p_vdoc, MarkdownConverterType p_type); + + // @p_text: text of fenced code block. + // Get the indent level of the first line (fence) and unindent the whole block + // to make the fence at the highest indent level. + // This operation is to make sure JS could handle the code block correctly + // without any context. + static QString unindentCodeBlock(const QString &p_text); + +private slots: + void handleCodeBlocksUpdated(TimeStamp p_timeStamp, const QVector &p_codeBlocks); + + void handleTextHighlightResult(const QString &p_html, int p_id, unsigned long long p_timeStamp); + +private: + struct HLResult + { + HLResult() + : m_timeStamp(0) + { + } + + HLResult(TimeStamp p_timeStamp, const QVector &p_units) + : m_timeStamp(p_timeStamp), + m_units(p_units) + { + } + + TimeStamp m_timeStamp; + + QVector m_units; + }; + + void parseHighlightResult(TimeStamp p_timeStamp, int p_idx, const QString &p_html); + + // @p_text: the raw text of the code block; + // @p_index: the start index of the span element within @p_text; + // @p_units: all the highlight units of this code block; + bool parseSpanElement(QXmlStreamReader &p_xml, + const QString &p_text, int &p_index, + QVector &p_units); + + void updateHighlightResults(TimeStamp p_timeStamp, int p_startPos, QVector p_units); + + void addToHighlightCache(const QString &p_text, + TimeStamp p_timeStamp, + const QVector &p_units); + + PegMarkdownHighlighter *m_highlighter; + VDocument *m_vdocument; + MarkdownConverterType m_type; + + TimeStamp m_timeStamp; + + QVector m_codeBlocks; + + // Cache for highlight result, using the code block text as key. + // The HLResult has relative position only. + QHash m_cache; +}; + +#endif // VCODEBLOCKHIGHLIGHTHELPER2_H diff --git a/src/vmdeditor.cpp b/src/vmdeditor.cpp index 424de083..d1bf8222 100644 --- a/src/vmdeditor.cpp +++ b/src/vmdeditor.cpp @@ -13,7 +13,7 @@ #include "utils/veditutils.h" #include "vedittab.h" #include "pegmarkdownhighlighter.h" -#include "vcodeblockhighlighthelper.h" +#include "vcodeblockhighlighthelper2.h" #include "vmdeditoperations.h" #include "vtableofcontent.h" #include "utils/veditutils.h" @@ -101,7 +101,7 @@ VMdEditor::VMdEditor(VFile *p_file, } }); - m_cbHighlighter = new VCodeBlockHighlightHelper(m_pegHighlighter, p_type); + m_cbHighlighter = new VCodeBlockHighlightHelper2(m_pegHighlighter, p_doc, p_type); m_previewMgr = new VPreviewManager(this, m_pegHighlighter); connect(m_pegHighlighter, &PegMarkdownHighlighter::imageLinksUpdated, diff --git a/src/vmdeditor.h b/src/vmdeditor.h index 15f9c43a..3167a0ee 100644 --- a/src/vmdeditor.h +++ b/src/vmdeditor.h @@ -17,7 +17,7 @@ #include "utils/vutils.h" class PegMarkdownHighlighter; -class VCodeBlockHighlightHelper; +class VCodeBlockHighlightHelper2; class VDocument; class VPreviewManager; class VCopyTextAsHtmlDialog; @@ -329,7 +329,7 @@ private: PegMarkdownHighlighter *m_pegHighlighter; - VCodeBlockHighlightHelper *m_cbHighlighter; + VCodeBlockHighlightHelper2 *m_cbHighlighter; VPreviewManager *m_previewMgr;