TextEditor: Support highlighter results that cross block boundaries

This is needed to properly highlight raw string literals.

Task-number: QTCREATORBUG-16183
Change-Id: I00c59a26891bd339b58cc515041d237e496d6068
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2020-10-19 14:49:06 +02:00
parent 80951d3e59
commit 526579b09d

View File

@@ -30,25 +30,52 @@
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QTextDocument>
#include <QTextBlock> #include <QTextBlock>
#include <QTextDocument>
#include <algorithm>
using namespace TextEditor; using namespace TextEditor;
using namespace TextEditor::SemanticHighlighter; using namespace TextEditor::SemanticHighlighter;
namespace { namespace {
QTextLayout::FormatRange rangeForResult(const HighlightingResult &result, class Range {
public:
QTextLayout::FormatRange formatRange;
QTextBlock block;
};
using Ranges = QVector<Range>;
Ranges rangesForResult(const HighlightingResult &result,
QTextDocument *doc,
const QHash<int, QTextCharFormat> &kindToFormat) const QHash<int, QTextCharFormat> &kindToFormat)
{ {
QTextLayout::FormatRange formatRange; const QTextCharFormat format = result.useTextSyles
formatRange.start = int(result.column) - 1;
formatRange.length = int(result.length);
formatRange.format = result.useTextSyles
? TextEditorSettings::fontSettings().toTextCharFormat(result.textStyles) ? TextEditorSettings::fontSettings().toTextCharFormat(result.textStyles)
: kindToFormat.value(result.kind); : kindToFormat.value(result.kind);
return formatRange; if (!format.isValid())
return {};
HighlightingResult curResult = result;
QTextBlock curBlock = doc->findBlockByNumber(curResult.line - 1);
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;
} }
} }
@@ -81,39 +108,22 @@ void SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
QTextDocument *doc = highlighter->document(); QTextDocument *doc = highlighter->document();
QTC_ASSERT(currentBlockNumber < doc->blockCount(), return); QTC_ASSERT(currentBlockNumber < doc->blockCount(), return);
QTextBlock b = doc->findBlockByNumber(currentBlockNumber); QTextBlock currentBlock = doc->findBlockByNumber(currentBlockNumber);
HighlightingResult result = future.resultAt(from); std::map<QTextBlock, QVector<QTextLayout::FormatRange>> formatRanges;
for (int i = from; i < to && b.isValid(); ) { for (int i = from; i < to; ++i) {
const int blockNumber = int(result.line) - 1; const Ranges ranges = rangesForResult(future.resultAt(i), doc, kindToFormat);
QTC_ASSERT(blockNumber < doc->blockCount(), return); for (const Range &range : ranges)
formatRanges[range.block].append(range.formatRange);
// clear formats of blocks until blockNumber
while (currentBlockNumber < blockNumber) {
highlighter->clearExtraFormats(b);
b = b.next();
++currentBlockNumber;
} }
// collect all the formats for the current line for (auto &[block, ranges] : formatRanges) {
QVector<QTextLayout::FormatRange> formats; while (currentBlock < block) {
formats.reserve(to - from); highlighter->clearExtraFormats(currentBlock);
forever { currentBlock = currentBlock.next();
const QTextLayout::FormatRange formatRange = rangeForResult(result, kindToFormat);
if (formatRange.format.isValid())
formats.append(formatRange);
++i;
if (i >= to)
break;
result = future.resultAt(i);
const int nextBlockNumber = int(result.line) - 1;
if (nextBlockNumber != blockNumber)
break;
} }
highlighter->setExtraFormats(b, std::move(formats)); highlighter->setExtraFormats(block, std::move(ranges));
b = b.next(); currentBlock = block.next();
++currentBlockNumber;
} }
} }
@@ -128,21 +138,16 @@ void SemanticHighlighter::setExtraAdditionalFormats(SyntaxHighlighter *highlight
QTextDocument *doc = highlighter->document(); QTextDocument *doc = highlighter->document();
QTC_ASSERT(doc, return ); QTC_ASSERT(doc, return );
QVector<QVector<QTextLayout::FormatRange>> ranges(doc->blockCount()); std::map<QTextBlock, QVector<QTextLayout::FormatRange>> formatRanges;
for (auto result : results) { for (auto result : results) {
const QTextLayout::FormatRange formatRange = rangeForResult(result, kindToFormat); const Ranges ranges = rangesForResult(result, doc, kindToFormat);
if (formatRange.format.isValid()) for (const Range &range : ranges)
ranges[int(result.line) - 1].append(formatRange); formatRanges[range.block].append(range.formatRange);
} }
for (int blockNumber = 0; blockNumber < ranges.count(); ++blockNumber) { for (auto &[block, ranges] : formatRanges)
if (!ranges[blockNumber].isEmpty()) { highlighter->setExtraFormats(block, std::move(ranges));
QTextBlock b = doc->findBlockByNumber(blockNumber);
QTC_ASSERT(b.isValid(), return );
highlighter->setExtraFormats(b, std::move(ranges[blockNumber]));
}
}
} }
void SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd( void SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(