forked from qt-creator/qt-creator
Use an elapsed timer in the syntax highlighter to periodically return
from the highlight and push a continue highlight to the end of the event
loop.
This allows the user to interact with the editor in between those
batches. If the user modifies the document in between highlighting
batches, the area that still needs a rehighlight is increased if needed.
This also reverts 62ea85ee6a and the
related changes.
Task-number: QTCREATORBUG-28727
Change-Id: I7c394dbdff658330bb72f3b68b9928980947db75
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
169 lines
5.7 KiB
C++
169 lines
5.7 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include "semantichighlighter.h"
|
|
|
|
#include "syntaxhighlighter.h"
|
|
#include "texteditorsettings.h"
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
#include <QTextBlock>
|
|
#include <QTextDocument>
|
|
|
|
#include <algorithm>
|
|
|
|
using namespace TextEditor;
|
|
using namespace TextEditor::SemanticHighlighter;
|
|
|
|
namespace {
|
|
|
|
class Range {
|
|
public:
|
|
QTextLayout::FormatRange formatRange;
|
|
QTextBlock block;
|
|
};
|
|
using Ranges = QVector<Range>;
|
|
|
|
const Ranges rangesForResult(const HighlightingResult &result, const QTextBlock &startBlock,
|
|
const QHash<int, QTextCharFormat> &kindToFormat)
|
|
{
|
|
const QTextCharFormat format = result.useTextSyles
|
|
? TextEditorSettings::fontSettings().toTextCharFormat(result.textStyles)
|
|
: kindToFormat.value(result.kind);
|
|
if (!format.isValid())
|
|
return {};
|
|
|
|
HighlightingResult curResult = result;
|
|
QTextBlock curBlock = startBlock;
|
|
Ranges ranges;
|
|
while (curBlock.isValid()) {
|
|
Range range;
|
|
range.block = curBlock;
|
|
range.formatRange.format = format;
|
|
range.formatRange.start = curResult.column - 1;
|
|
range.formatRange.length = std::min(curResult.length,
|
|
curBlock.length() - range.formatRange.start);
|
|
ranges << range;
|
|
if (range.formatRange.length == curResult.length)
|
|
break;
|
|
curBlock = curBlock.next();
|
|
curResult.column = 1;
|
|
curResult.length -= range.formatRange.length;
|
|
}
|
|
|
|
return ranges;
|
|
}
|
|
|
|
const Ranges rangesForResult(
|
|
const HighlightingResult &result,
|
|
QTextDocument *doc,
|
|
const QHash<int, QTextCharFormat> &kindToFormat,
|
|
const Splitter &splitter = {})
|
|
{
|
|
const QTextBlock startBlock = doc->findBlockByNumber(result.line - 1);
|
|
if (splitter) {
|
|
Ranges ranges;
|
|
for (const auto &[newResult, newBlock] : splitter(result, startBlock))
|
|
ranges << rangesForResult(newResult, newBlock, kindToFormat);
|
|
return ranges;
|
|
}
|
|
return rangesForResult(result, startBlock, kindToFormat);
|
|
}
|
|
|
|
}
|
|
|
|
void SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
|
|
SyntaxHighlighter *highlighter,
|
|
const QFuture<HighlightingResult> &future,
|
|
int from,
|
|
int to,
|
|
const QHash<int, QTextCharFormat> &kindToFormat,
|
|
const Splitter &splitter)
|
|
{
|
|
if (to <= from)
|
|
return;
|
|
|
|
const int resultStartLine = future.resultAt(from).line;
|
|
int formattingStartLine = 1;
|
|
|
|
// Find the line on which to start formatting, where "formatting" means to either
|
|
// clear out formats from outdated document versions (if there is no current result
|
|
// on that line), or apply the format corresponding to the respective result.
|
|
// Note that if there are earlier results on the same line, we have to make sure they
|
|
// get re-applied by adapting the from variable accordingly.
|
|
for (int i = from - 1; i >= 0; --i) {
|
|
const HighlightingResult &result = future.resultAt(i);
|
|
if (result.line == resultStartLine) {
|
|
from = i;
|
|
} else if (result.line < resultStartLine) {
|
|
formattingStartLine = result.line + 1;
|
|
from = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
QTextDocument *doc = highlighter->document();
|
|
QTC_ASSERT(formattingStartLine <= doc->blockCount(), return);
|
|
QTextBlock currentBlock = doc->findBlockByNumber(formattingStartLine - 1);
|
|
|
|
std::map<QTextBlock, QVector<QTextLayout::FormatRange>> formatRanges;
|
|
for (int i = from; i < to; ++i) {
|
|
for (const Range &range : rangesForResult(future.resultAt(i), doc, kindToFormat, splitter))
|
|
formatRanges[range.block].append(range.formatRange);
|
|
}
|
|
|
|
for (auto &[block, ranges] : formatRanges) {
|
|
while (currentBlock < block) {
|
|
highlighter->clearExtraFormats(currentBlock);
|
|
currentBlock = currentBlock.next();
|
|
}
|
|
highlighter->setExtraFormats(block, std::move(ranges));
|
|
currentBlock = block.next();
|
|
}
|
|
}
|
|
|
|
void SemanticHighlighter::setExtraAdditionalFormats(SyntaxHighlighter *highlighter,
|
|
const QList<HighlightingResult> &results,
|
|
const QHash<int, QTextCharFormat> &kindToFormat)
|
|
{
|
|
if (!highlighter)
|
|
return;
|
|
highlighter->clearAllExtraFormats();
|
|
|
|
QTextDocument *doc = highlighter->document();
|
|
QTC_ASSERT(doc, return );
|
|
|
|
std::map<QTextBlock, QVector<QTextLayout::FormatRange>> formatRanges;
|
|
|
|
for (auto result : results) {
|
|
for (const Range &range : rangesForResult(result, doc, kindToFormat))
|
|
formatRanges[range.block].append(range.formatRange);
|
|
}
|
|
|
|
for (auto &[block, ranges] : formatRanges)
|
|
highlighter->setExtraFormats(block, std::move(ranges));
|
|
}
|
|
|
|
void SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(
|
|
SyntaxHighlighter *highlighter, const QFuture<HighlightingResult> &future)
|
|
{
|
|
const QTextDocument * const doc = highlighter->document();
|
|
QTextBlock firstBlockToClear = doc->begin();
|
|
for (int i = future.resultCount() - 1; i >= 0; --i) {
|
|
const HighlightingResult &result = future.resultAt(i);
|
|
if (result.line) {
|
|
const QTextBlock blockForLine = doc->findBlockByNumber(result.line - 1);
|
|
const QTextBlock lastBlockWithResults = doc->findBlock(
|
|
blockForLine.position() + result.column - 1 + result.length);
|
|
firstBlockToClear = lastBlockWithResults.next();
|
|
break;
|
|
}
|
|
}
|
|
|
|
QList<int> clearBlockNumberVector;
|
|
for (QTextBlock b = firstBlockToClear; b.isValid(); b = b.next())
|
|
highlighter->clearExtraFormats(b);
|
|
|
|
}
|