mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 22:09:52 +08:00
PegHighlighter: refine fast parse
- Fast parse block range: look upward till the indentation is 0; - Rehighlight all the fast-parsed blocks explicitly; - Do not reset block user state by default; - Pre highlight single format blocks to avoid jitter of line height;
This commit is contained in:
parent
59efed94f3
commit
48db50fd5e
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "pegparser.h"
|
#include "pegparser.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
@ -69,7 +68,7 @@ void PegMarkdownHighlighter::init(const QVector<HighlightingStyle> &p_styles,
|
|||||||
connect(m_fastParseTimer, &QTimer::timeout,
|
connect(m_fastParseTimer, &QTimer::timeout,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
QSharedPointer<PegHighlighterFastResult> result(m_fastResult);
|
QSharedPointer<PegHighlighterFastResult> result(m_fastResult);
|
||||||
if (!result->matched(m_timeStamp)) {
|
if (!result->matched(m_timeStamp) || m_result->matched(m_timeStamp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,18 +88,26 @@ void PegMarkdownHighlighter::highlightBlock(const QString &p_text)
|
|||||||
{
|
{
|
||||||
QSharedPointer<PegHighlighterResult> result(m_result);
|
QSharedPointer<PegHighlighterResult> result(m_result);
|
||||||
|
|
||||||
int blockNum = currentBlock().blockNumber();
|
QTextBlock block = currentBlock();
|
||||||
|
int blockNum = block.blockNumber();
|
||||||
|
|
||||||
|
if (result->matched(m_timeStamp)) {
|
||||||
|
preHighlightMonospaceBlock(result->m_blocksHighlights, blockNum, p_text);
|
||||||
|
} else {
|
||||||
|
preHighlightMonospaceBlock(m_fastResult->m_blocksHighlights, blockNum, p_text);
|
||||||
|
}
|
||||||
|
|
||||||
highlightBlockOne(result->m_blocksHighlights, blockNum);
|
highlightBlockOne(result->m_blocksHighlights, blockNum);
|
||||||
|
|
||||||
highlightBlockOne(m_fastResult->m_blocksHighlights, blockNum);
|
// The complete result is not ready yet. We use fast result for compensation.
|
||||||
|
if (!result->matched(m_timeStamp)) {
|
||||||
|
highlightBlockOne(m_fastResult->m_blocksHighlights, blockNum);
|
||||||
|
}
|
||||||
|
|
||||||
// Set current block's user data.
|
// Set current block's user data.
|
||||||
updateBlockUserData(blockNum, p_text);
|
updateBlockUserData(blockNum, p_text);
|
||||||
|
|
||||||
setCurrentBlockState(HighlightBlockState::Normal);
|
updateBlockUserState(result, blockNum, p_text);
|
||||||
|
|
||||||
updateCodeBlockState(result, blockNum, p_text);
|
|
||||||
|
|
||||||
if (currentBlockState() == HighlightBlockState::CodeBlock) {
|
if (currentBlockState() == HighlightBlockState::CodeBlock) {
|
||||||
highlightCodeBlock(result, blockNum, p_text);
|
highlightCodeBlock(result, blockNum, p_text);
|
||||||
@ -109,6 +116,30 @@ void PegMarkdownHighlighter::highlightBlock(const QString &p_text)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::preHighlightMonospaceBlock(const QVector<QVector<HLUnit>> &p_highlights,
|
||||||
|
int p_blockNum,
|
||||||
|
const QString &p_text)
|
||||||
|
{
|
||||||
|
int sz = p_text.size();
|
||||||
|
if (sz == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_singleFormatBlocks.contains(p_blockNum)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_highlights.size() > p_blockNum) {
|
||||||
|
const QVector<HLUnit> &units = p_highlights[p_blockNum];
|
||||||
|
if (units.size() == 1) {
|
||||||
|
const HLUnit &unit = units[0];
|
||||||
|
if (unit.start == 0 && (int)unit.length < sz) {
|
||||||
|
setFormat(unit.length, sz - unit.length, m_styles[unit.styleIndex].format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PegMarkdownHighlighter::highlightBlockOne(const QVector<QVector<HLUnit>> &p_highlights,
|
void PegMarkdownHighlighter::highlightBlockOne(const QVector<QVector<HLUnit>> &p_highlights,
|
||||||
int p_blockNum)
|
int p_blockNum)
|
||||||
{
|
{
|
||||||
@ -145,6 +176,7 @@ void PegMarkdownHighlighter::highlightBlockOne(const QVector<QVector<HLUnit>> &p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// highlightBlock() will be called before this function.
|
||||||
void PegMarkdownHighlighter::handleContentsChange(int p_position, int p_charsRemoved, int p_charsAdded)
|
void PegMarkdownHighlighter::handleContentsChange(int p_position, int p_charsRemoved, int p_charsAdded)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p_position);
|
Q_UNUSED(p_position);
|
||||||
@ -210,6 +242,8 @@ void PegMarkdownHighlighter::processFastParseResult(const QSharedPointer<PegPars
|
|||||||
{
|
{
|
||||||
m_fastParseTimer->stop();
|
m_fastParseTimer->stop();
|
||||||
m_fastResult.reset(new PegHighlighterFastResult(this, p_result));
|
m_fastResult.reset(new PegHighlighterFastResult(this, p_result));
|
||||||
|
// Add additional single format blocks.
|
||||||
|
updateSingleFormatBlocks(m_fastResult->m_blocksHighlights);
|
||||||
m_fastParseTimer->start();
|
m_fastParseTimer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +262,6 @@ void PegMarkdownHighlighter::setCodeBlockHighlights(TimeStamp p_timeStamp,
|
|||||||
const QVector<HLUnitPos> &p_units)
|
const QVector<HLUnitPos> &p_units)
|
||||||
{
|
{
|
||||||
QSharedPointer<PegHighlighterResult> result(m_result);
|
QSharedPointer<PegHighlighterResult> result(m_result);
|
||||||
|
|
||||||
if (!result->matched(p_timeStamp)) {
|
if (!result->matched(p_timeStamp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -293,7 +326,13 @@ void PegMarkdownHighlighter::setCodeBlockHighlights(TimeStamp p_timeStamp,
|
|||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (--result->m_numOfCodeBlockHighlightsToRecv <= 0) {
|
if (--result->m_numOfCodeBlockHighlightsToRecv <= 0) {
|
||||||
rehighlight();
|
// Rehighlight specific blocks.
|
||||||
|
const QVector<QVector<HLUnitStyle>> &hls = result->m_codeBlocksHighlights;
|
||||||
|
for (int i = 0; i < hls.size(); ++i) {
|
||||||
|
if (!hls[i].isEmpty()) {
|
||||||
|
rehighlightBlock(m_doc->findBlockByNumber(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,17 +353,35 @@ void PegMarkdownHighlighter::handleParseResult(const QSharedPointer<PegParseResu
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PegHighlighterResult *pegRes = new PegHighlighterResult(this, p_result);
|
m_result.reset(new PegHighlighterResult(this, p_result));
|
||||||
m_result.reset(pegRes);
|
|
||||||
|
m_singleFormatBlocks.clear();
|
||||||
|
|
||||||
|
updateSingleFormatBlocks(m_result->m_blocksHighlights);
|
||||||
|
|
||||||
updateCodeBlocks(m_result);
|
updateCodeBlocks(m_result);
|
||||||
|
|
||||||
// Now we got a new result, rehighlight.
|
|
||||||
rehighlight();
|
rehighlight();
|
||||||
|
|
||||||
completeHighlight(m_result);
|
completeHighlight(m_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PegMarkdownHighlighter::updateSingleFormatBlocks(const QVector<QVector<HLUnit>> &p_highlights)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < p_highlights.size(); ++i) {
|
||||||
|
const QVector<HLUnit> &units = p_highlights[i];
|
||||||
|
if (units.size() == 1) {
|
||||||
|
const HLUnit &unit = units[0];
|
||||||
|
if (unit.start == 0 && unit.length > 0) {
|
||||||
|
QTextBlock block = m_doc->findBlockByNumber(i);
|
||||||
|
if (block.length() - 1 == unit.length) {
|
||||||
|
m_singleFormatBlocks.insert(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PegMarkdownHighlighter::updateCodeBlocks(QSharedPointer<PegHighlighterResult> p_result)
|
void PegMarkdownHighlighter::updateCodeBlocks(QSharedPointer<PegHighlighterResult> p_result)
|
||||||
{
|
{
|
||||||
if (!p_result->matched(m_timeStamp)) {
|
if (!p_result->matched(m_timeStamp)) {
|
||||||
@ -357,20 +414,27 @@ void PegMarkdownHighlighter::updateBlockUserData(int p_blockNum, const QString &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PegMarkdownHighlighter::updateCodeBlockState(const QSharedPointer<PegHighlighterResult> &p_result,
|
void PegMarkdownHighlighter::updateBlockUserState(const QSharedPointer<PegHighlighterResult> &p_result,
|
||||||
int p_blockNum,
|
int p_blockNum,
|
||||||
const QString &p_text)
|
const QString &p_text)
|
||||||
{
|
{
|
||||||
|
// Newly-added block.
|
||||||
|
if (currentBlockState() == -1) {
|
||||||
|
setCurrentBlockState(HighlightBlockState::Normal);
|
||||||
|
}
|
||||||
|
|
||||||
if (!p_result->matched(m_timeStamp)) {
|
if (!p_result->matched(m_timeStamp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HighlightBlockState state = HighlightBlockState::Normal;
|
||||||
|
|
||||||
auto it = p_result->m_codeBlocksState.find(p_blockNum);
|
auto it = p_result->m_codeBlocksState.find(p_blockNum);
|
||||||
if (it != p_result->m_codeBlocksState.end()) {
|
if (it != p_result->m_codeBlocksState.end()) {
|
||||||
VTextBlockData *blockData = currentBlockData();
|
VTextBlockData *blockData = currentBlockData();
|
||||||
Q_ASSERT(blockData);
|
Q_ASSERT(blockData);
|
||||||
|
|
||||||
HighlightBlockState state = it.value();
|
state = it.value();
|
||||||
// Set code block indentation.
|
// Set code block indentation.
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case HighlightBlockState::CodeBlockStart:
|
case HighlightBlockState::CodeBlockStart:
|
||||||
@ -404,10 +468,10 @@ void PegMarkdownHighlighter::updateCodeBlockState(const QSharedPointer<PegHighli
|
|||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set code block state.
|
|
||||||
setCurrentBlockState(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set code block state.
|
||||||
|
setCurrentBlockState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PegMarkdownHighlighter::highlightCodeBlock(const QSharedPointer<PegHighlighterResult> &p_result,
|
void PegMarkdownHighlighter::highlightCodeBlock(const QSharedPointer<PegHighlighterResult> &p_result,
|
||||||
@ -523,15 +587,14 @@ void PegMarkdownHighlighter::getFastParseBlockRange(int p_position,
|
|||||||
|
|
||||||
// Look up.
|
// Look up.
|
||||||
// Find empty block.
|
// Find empty block.
|
||||||
if (!VEditUtils::isEmptyBlock(firstBlock)) {
|
// When firstBlock is an empty block at first, we should always skip it.
|
||||||
while (firstBlock.isValid() && num < maxNumOfBlocks) {
|
while (firstBlock.isValid() && num < maxNumOfBlocks) {
|
||||||
QTextBlock block = firstBlock.previous();
|
QTextBlock block = firstBlock.previous();
|
||||||
if (block.isValid() && !VEditUtils::isEmptyBlock(block)) {
|
if (block.isValid() && !VEditUtils::isEmptyBlock(block)) {
|
||||||
firstBlock = block;
|
firstBlock = block;
|
||||||
++num;
|
++num;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,13 +615,15 @@ void PegMarkdownHighlighter::getFastParseBlockRange(int p_position,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look down.
|
// Till the block with 0 indentation to handle contents in list.
|
||||||
// Find empty block.
|
while (firstBlock.isValid() && num < maxNumOfBlocks) {
|
||||||
if (!VEditUtils::isEmptyBlock(lastBlock)) {
|
if (VEditUtils::fetchIndentation(firstBlock) == 0
|
||||||
while (lastBlock.isValid() && num < maxNumOfBlocks) {
|
&& !VEditUtils::isEmptyBlock(firstBlock)) {
|
||||||
QTextBlock block = lastBlock.next();
|
break;
|
||||||
if (block.isValid() && !VEditUtils::isEmptyBlock(block)) {
|
} else {
|
||||||
lastBlock = block;
|
QTextBlock block = firstBlock.previous();
|
||||||
|
if (block.isValid()) {
|
||||||
|
firstBlock = block;
|
||||||
++num;
|
++num;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@ -566,6 +631,19 @@ void PegMarkdownHighlighter::getFastParseBlockRange(int p_position,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Look down.
|
||||||
|
// Find empty block.
|
||||||
|
// If lastBlock is an empty block at first, we should always skip it.
|
||||||
|
while (lastBlock.isValid() && num < maxNumOfBlocks) {
|
||||||
|
QTextBlock block = lastBlock.next();
|
||||||
|
if (block.isValid() && !VEditUtils::isEmptyBlock(block)) {
|
||||||
|
lastBlock = block;
|
||||||
|
++num;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cross code block.
|
// Cross code block.
|
||||||
while (lastBlock.isValid() && num < maxNumOfBlocks) {
|
while (lastBlock.isValid() && num < maxNumOfBlocks) {
|
||||||
int state = lastBlock.userState();
|
int state = lastBlock.userState();
|
||||||
|
@ -81,7 +81,7 @@ private:
|
|||||||
// Set the user data of currentBlock().
|
// Set the user data of currentBlock().
|
||||||
void updateBlockUserData(int p_blockNum, const QString &p_text);
|
void updateBlockUserData(int p_blockNum, const QString &p_text);
|
||||||
|
|
||||||
void updateCodeBlockState(const QSharedPointer<PegHighlighterResult> &p_result,
|
void updateBlockUserState(const QSharedPointer<PegHighlighterResult> &p_result,
|
||||||
int p_blockNum,
|
int p_blockNum,
|
||||||
const QString &p_text);
|
const QString &p_text);
|
||||||
|
|
||||||
@ -112,6 +112,13 @@ private:
|
|||||||
void highlightBlockOne(const QVector<QVector<HLUnit>> &p_highlights,
|
void highlightBlockOne(const QVector<QVector<HLUnit>> &p_highlights,
|
||||||
int p_blockNum);
|
int p_blockNum);
|
||||||
|
|
||||||
|
// To avoid line height jitter.
|
||||||
|
void preHighlightMonospaceBlock(const QVector<QVector<HLUnit>> &p_highlights,
|
||||||
|
int p_blockNum,
|
||||||
|
const QString &p_text);
|
||||||
|
|
||||||
|
void updateSingleFormatBlocks(const QVector<QVector<HLUnit>> &p_highlights);
|
||||||
|
|
||||||
QTextDocument *m_doc;
|
QTextDocument *m_doc;
|
||||||
|
|
||||||
TimeStamp m_timeStamp;
|
TimeStamp m_timeStamp;
|
||||||
@ -138,6 +145,9 @@ private:
|
|||||||
QTimer *m_timer;
|
QTimer *m_timer;
|
||||||
|
|
||||||
QTimer *m_fastParseTimer;
|
QTimer *m_fastParseTimer;
|
||||||
|
|
||||||
|
// Blocks have only one format set which occupies the whole block.
|
||||||
|
QSet<int> m_singleFormatBlocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline const QVector<VElementRegion> &PegMarkdownHighlighter::getHeaderRegions() const
|
inline const QVector<VElementRegion> &PegMarkdownHighlighter::getHeaderRegions() const
|
||||||
|
@ -106,7 +106,7 @@ minimize_to_system_tray=-1
|
|||||||
markdown_suffix=md,markdown,mkd
|
markdown_suffix=md,markdown,mkd
|
||||||
|
|
||||||
; Markdown highlight timer interval (milliseconds)
|
; Markdown highlight timer interval (milliseconds)
|
||||||
markdown_highlight_interval=200
|
markdown_highlight_interval=400
|
||||||
|
|
||||||
; Adds specified height between lines (in pixels)
|
; Adds specified height between lines (in pixels)
|
||||||
line_distance_height=3
|
line_distance_height=3
|
||||||
|
@ -956,6 +956,11 @@ int VEditUtils::fetchIndentation(const QString &p_text)
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VEditUtils::fetchIndentation(const QTextBlock &p_block)
|
||||||
|
{
|
||||||
|
return fetchIndentation(p_block.text());
|
||||||
|
}
|
||||||
|
|
||||||
void VEditUtils::insertBlock(QTextCursor &p_cursor,
|
void VEditUtils::insertBlock(QTextCursor &p_cursor,
|
||||||
bool p_above)
|
bool p_above)
|
||||||
{
|
{
|
||||||
|
@ -186,6 +186,8 @@ public:
|
|||||||
|
|
||||||
static int fetchIndentation(const QString &p_text);
|
static int fetchIndentation(const QString &p_text);
|
||||||
|
|
||||||
|
static int fetchIndentation(const QTextBlock &p_block);
|
||||||
|
|
||||||
// Insert a block above/below current block. Move the cursor to the start of
|
// Insert a block above/below current block. Move the cursor to the start of
|
||||||
// the new block after insertion.
|
// the new block after insertion.
|
||||||
static void insertBlock(QTextCursor &p_cursor,
|
static void insertBlock(QTextCursor &p_cursor,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user