forked from qt-creator/qt-creator
TextEditor: correctly highlight preedit text
Fixes: QTCREATORBUG-29134 Change-Id: I8c5cdab8c5b2e5a2380c9e4aeadaf1bd72e60e09 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
@@ -185,6 +185,28 @@ void GenerigHighlighterTests::testChange()
|
|||||||
compareFormats(actualFormats.at(i), formatRanges.at(i));
|
compareFormats(actualFormats.at(i), formatRanges.at(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GenerigHighlighterTests::testPreeditText()
|
||||||
|
{
|
||||||
|
QTextBlock block = m_editor->textDocument()->document()->findBlockByNumber(2);
|
||||||
|
QVERIFY(block.isValid());
|
||||||
|
|
||||||
|
block.layout()->setPreeditArea(7, "uaf");
|
||||||
|
m_editor->textDocument()->syntaxHighlighter()->rehighlight();
|
||||||
|
|
||||||
|
const FormatRanges formatRanges = {{0, 4, toFormat(C_VISUAL_WHITESPACE)},
|
||||||
|
{4, 3, toFormat(C_TYPE)},
|
||||||
|
{10, 3, toFormat(C_TYPE)},
|
||||||
|
{13, 1, toFormat(C_FUNCTION)},
|
||||||
|
{14, 1, toFormat(C_VISUAL_WHITESPACE)},
|
||||||
|
{15, 6, toFormat(C_STRING)},
|
||||||
|
{21, 1, toFormat(C_FUNCTION)}};
|
||||||
|
const QList<QTextLayout::FormatRange> actualFormats = block.layout()->formats();
|
||||||
|
// full hash calculation for QTextCharFormat fails so just check the important entries of format
|
||||||
|
QCOMPARE(actualFormats.size(), formatRanges.size());
|
||||||
|
for (int i = 0; i < formatRanges.size(); ++i)
|
||||||
|
compareFormats(actualFormats.at(i), formatRanges.at(i));
|
||||||
|
}
|
||||||
|
|
||||||
void GenerigHighlighterTests::cleanupTestCase()
|
void GenerigHighlighterTests::cleanupTestCase()
|
||||||
{
|
{
|
||||||
if (m_editor)
|
if (m_editor)
|
||||||
|
@@ -17,6 +17,7 @@ private slots:
|
|||||||
void testHighlight_data();
|
void testHighlight_data();
|
||||||
void testHighlight();
|
void testHighlight();
|
||||||
void testChange();
|
void testChange();
|
||||||
|
void testPreeditText();
|
||||||
void cleanupTestCase();
|
void cleanupTestCase();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@@ -17,6 +17,12 @@
|
|||||||
|
|
||||||
namespace TextEditor {
|
namespace TextEditor {
|
||||||
|
|
||||||
|
enum HighlighterTypeProperty
|
||||||
|
{
|
||||||
|
SyntaxHighlight = QTextFormat::UserProperty + 1,
|
||||||
|
SemanticHighlight = QTextFormat::UserProperty + 2
|
||||||
|
};
|
||||||
|
|
||||||
class SyntaxHighlighterPrivate
|
class SyntaxHighlighterPrivate
|
||||||
{
|
{
|
||||||
SyntaxHighlighter *q_ptr = nullptr;
|
SyntaxHighlighter *q_ptr = nullptr;
|
||||||
@@ -98,9 +104,9 @@ void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, in
|
|||||||
|
|
||||||
QVector<QTextLayout::FormatRange> ranges;
|
QVector<QTextLayout::FormatRange> ranges;
|
||||||
QVector<QTextLayout::FormatRange> oldRanges;
|
QVector<QTextLayout::FormatRange> oldRanges;
|
||||||
std::tie(ranges, oldRanges)
|
std::tie(oldRanges, ranges)
|
||||||
= Utils::partition(layout->formats(), [](const QTextLayout::FormatRange &range) {
|
= Utils::partition(layout->formats(), [](const QTextLayout::FormatRange &range) {
|
||||||
return range.format.property(QTextFormat::UserProperty).toBool();
|
return range.format.property(SyntaxHighlight).toBool();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (currentBlock.contains(from)) {
|
if (currentBlock.contains(from)) {
|
||||||
@@ -129,8 +135,23 @@ void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, in
|
|||||||
while (i < formatChanges.count() && formatChanges.at(i) == r.format)
|
while (i < formatChanges.count() && formatChanges.at(i) == r.format)
|
||||||
++i;
|
++i;
|
||||||
|
|
||||||
|
r.format.setProperty(SyntaxHighlight, true);
|
||||||
r.length = i - r.start;
|
r.length = i - r.start;
|
||||||
|
|
||||||
|
const QString preeditText = currentBlock.layout()->preeditAreaText();
|
||||||
|
if (!preeditText.isEmpty()) {
|
||||||
|
const int preeditPosition = currentBlock.layout()->preeditAreaPosition();
|
||||||
|
if (r.start >= preeditPosition) {
|
||||||
|
r.start += preeditText.length();
|
||||||
|
} else if (r.start + r.length > preeditPosition) {
|
||||||
|
QTextLayout::FormatRange beforePreeditRange = r;
|
||||||
|
r.start = preeditPosition + preeditText.length();
|
||||||
|
r.length = r.length - (r.start - preeditPosition);
|
||||||
|
beforePreeditRange.length = preeditPosition - beforePreeditRange.start;
|
||||||
|
newRanges << beforePreeditRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newRanges << r;
|
newRanges << r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,6 +677,24 @@ void SyntaxHighlighter::setExtraFormats(const QTextBlock &block,
|
|||||||
if (block.layout() == nullptr || blockLength == 0)
|
if (block.layout() == nullptr || blockLength == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const QString preeditText = block.layout()->preeditAreaText();
|
||||||
|
if (!preeditText.isEmpty()) {
|
||||||
|
QVector<QTextLayout::FormatRange> additionalRanges;
|
||||||
|
const int preeditPosition = block.layout()->preeditAreaPosition();
|
||||||
|
for (QTextLayout::FormatRange &r : formats) {
|
||||||
|
if (r.start >= preeditPosition) {
|
||||||
|
r.start += preeditText.length();
|
||||||
|
} else if (r.start + r.length > preeditPosition) {
|
||||||
|
QTextLayout::FormatRange afterPreeditRange = r;
|
||||||
|
afterPreeditRange.start = preeditPosition + preeditText.length();
|
||||||
|
afterPreeditRange.length = r.length - (preeditPosition - r.start);
|
||||||
|
additionalRanges << afterPreeditRange;
|
||||||
|
r.length = preeditPosition - r.start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formats << additionalRanges;
|
||||||
|
}
|
||||||
|
|
||||||
Utils::sort(formats, byStartOfRange);
|
Utils::sort(formats, byStartOfRange);
|
||||||
|
|
||||||
const QVector<QTextLayout::FormatRange> all = block.layout()->formats();
|
const QVector<QTextLayout::FormatRange> all = block.layout()->formats();
|
||||||
@@ -663,11 +702,11 @@ void SyntaxHighlighter::setExtraFormats(const QTextBlock &block,
|
|||||||
QVector<QTextLayout::FormatRange> formatsToApply;
|
QVector<QTextLayout::FormatRange> formatsToApply;
|
||||||
std::tie(previousSemanticFormats, formatsToApply)
|
std::tie(previousSemanticFormats, formatsToApply)
|
||||||
= Utils::partition(all, [](const QTextLayout::FormatRange &r) {
|
= Utils::partition(all, [](const QTextLayout::FormatRange &r) {
|
||||||
return r.format.hasProperty(QTextFormat::UserProperty);
|
return r.format.property(SemanticHighlight).toBool();
|
||||||
});
|
});
|
||||||
|
|
||||||
for (auto &format : formats)
|
for (auto &format : formats)
|
||||||
format.format.setProperty(QTextFormat::UserProperty, true);
|
format.format.setProperty(SemanticHighlight, true);
|
||||||
|
|
||||||
if (formats.size() == previousSemanticFormats.size()) {
|
if (formats.size() == previousSemanticFormats.size()) {
|
||||||
Utils::sort(previousSemanticFormats, byStartOfRange);
|
Utils::sort(previousSemanticFormats, byStartOfRange);
|
||||||
@@ -694,7 +733,7 @@ void SyntaxHighlighter::clearExtraFormats(const QTextBlock &block)
|
|||||||
|
|
||||||
const QVector<QTextLayout::FormatRange> formatsToApply
|
const QVector<QTextLayout::FormatRange> formatsToApply
|
||||||
= Utils::filtered(block.layout()->formats(), [](const QTextLayout::FormatRange &r) {
|
= Utils::filtered(block.layout()->formats(), [](const QTextLayout::FormatRange &r) {
|
||||||
return !r.format.hasProperty(QTextFormat::UserProperty);
|
return !r.format.property(SemanticHighlight).toBool();
|
||||||
});
|
});
|
||||||
|
|
||||||
bool wasInReformatBlocks = d->inReformatBlocks;
|
bool wasInReformatBlocks = d->inReformatBlocks;
|
||||||
|
@@ -26,6 +26,7 @@ private slots:
|
|||||||
void test_setExtraAdditionalFormats();
|
void test_setExtraAdditionalFormats();
|
||||||
void test_clearExtraFormats();
|
void test_clearExtraFormats();
|
||||||
void test_incrementalApplyAdditionalFormats();
|
void test_incrementalApplyAdditionalFormats();
|
||||||
|
void test_preeditText();
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -235,6 +236,7 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
|
|||||||
formatHash);
|
formatHash);
|
||||||
|
|
||||||
formats = firstBlock.layout()->formats();
|
formats = firstBlock.layout()->formats();
|
||||||
|
QCOMPARE(formats.size(), 1);
|
||||||
QCOMPARE(formats.at(0).format.fontItalic(), true);
|
QCOMPARE(formats.at(0).format.fontItalic(), true);
|
||||||
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||||
QCOMPARE(formats.at(0).start, 0);
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
@@ -338,6 +340,28 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
|
|||||||
QCOMPARE(formats.at(1).length, 2);
|
QCOMPARE(formats.at(1).length, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_highlighter::test_preeditText()
|
||||||
|
{
|
||||||
|
QCOMPARE(doc->blockCount(), 4);
|
||||||
|
|
||||||
|
QTextBlock firstBlock = doc->findBlockByNumber(0);
|
||||||
|
firstBlock.layout()->setPreeditArea(2, "uaf");
|
||||||
|
|
||||||
|
SemanticHighlighter::setExtraAdditionalFormats(highlighter, highlightingResults(), formatHash);
|
||||||
|
|
||||||
|
auto formats = firstBlock.layout()->formats();
|
||||||
|
QCOMPARE(formats.size(), 2);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), true);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
|
QCOMPARE(formats.at(0).length, 2);
|
||||||
|
|
||||||
|
QCOMPARE(formats.at(1).format.fontItalic(), true);
|
||||||
|
QCOMPARE(formats.at(1).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(1).start, 5);
|
||||||
|
QCOMPARE(formats.at(1).length, 3);
|
||||||
|
}
|
||||||
|
|
||||||
void tst_highlighter::cleanup()
|
void tst_highlighter::cleanup()
|
||||||
{
|
{
|
||||||
delete doc;
|
delete doc;
|
||||||
|
Reference in New Issue
Block a user