SyntaxHighlighter: Move SyntaxHighlighter to separate thread

This change involves the relocation of SyntaxHighlighter processing
to another thread. The core idea is to create a duplicate of the
original TextDocument using SyntaxHighlighterRunnerPrivate::cloneDocument.
A new SyntaxHighlighter is then instantiated by SyntaxHighLighterCreator
for the cloned document. The entire SyntaxHighLighterCreator class is
moved to a new thread, where it performs highlighting on the cloned
document. Upon completion of the highlighting process, the resultsReady
signal is emitted, and the updated highlighting data is applied to the
original document.

This shift of SyntaxHighlighter to another thread enhances the user
experience by preventing UI slowdowns during the highlighting process.

- Introduction of BaseSyntaxHighlighterRunner as an interface class for
future *SyntaxHighlighterRunner.
- Inclusion of DirectSyntaxHighlighterRunner class for performing
highlighting in the main thread, suitable for syntax highlighters
that cannot be moved to another thread.
- Introduction of ThreadedSyntaxHighlighterRunner class for highlighting
in a separate thread, preventing UI blocking during the process.
- Addition of Result data to the SyntaxHighlighter class to facilitate
data exchange between threads.

Task-number: QTCREATORBUG-28727
Change-Id: I4b6a38d15f5ec9b8828055d38d2a0c6f21a657b4
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Artem Sokolovskii
2023-05-08 17:11:54 +02:00
parent d5ddf391c1
commit 62ea85ee6a
33 changed files with 603 additions and 142 deletions

View File

@@ -131,7 +131,7 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(TextEditor::ICodeStylePreferenc
d->preview->setPlainText(QLatin1String(CppEditor::Constants::DEFAULT_CODE_STYLE_SNIPPETS[0]));
d->preview->textDocument()->setIndenter(new ClangFormatIndenter(d->preview->document()));
d->preview->textDocument()->setFontSettings(TextEditor::TextEditorSettings::fontSettings());
d->preview->textDocument()->setSyntaxHighlighterCreator(
d->preview->textDocument()->resetSyntaxHighlighter(
[] { return new CppEditor::CppHighlighter(); });
d->preview->textDocument()->indenter()->setFileName(fileName);

View File

@@ -22,6 +22,7 @@
#include <texteditor/storagesettings.h>
#include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/syntaxhighlighterrunner.h>
#include <utils/infobar.h>
#include <utils/mimeconstants.h>
@@ -79,7 +80,7 @@ private:
CppEditorDocument::CppEditorDocument()
{
setId(CppEditor::Constants::CPPEDITOR_ID);
setSyntaxHighlighterCreator([] { return new CppHighlighter(); });
resetSyntaxHighlighter([] { return new CppHighlighter(); });
ICodeStylePreferencesFactory *factory
= TextEditorSettings::codeStyleFactory(Constants::CPP_SETTINGS_ID);
@@ -165,7 +166,7 @@ QByteArray CppEditorDocument::contentsText() const
void CppEditorDocument::applyFontSettings()
{
if (TextEditor::SyntaxHighlighter *highlighter = syntaxHighlighter())
if (TextEditor::BaseSyntaxHighlighterRunner *highlighter = syntaxHighlighterRunner())
highlighter->clearAllExtraFormats(); // Clear all additional formats since they may have changed
TextDocument::applyFontSettings(); // rehighlights and updates additional formats
if (m_processor)
@@ -409,8 +410,8 @@ BaseEditorDocumentProcessor *CppEditorDocument::processor()
connect(m_processor.data(), &BaseEditorDocumentProcessor::cppDocumentUpdated, this,
[this](const CPlusPlus::Document::Ptr document) {
// Update syntax highlighter
auto *highlighter = qobject_cast<CppHighlighter *>(syntaxHighlighter());
highlighter->setLanguageFeatures(document->languageFeatures());
if (BaseSyntaxHighlighterRunner *highlighter = syntaxHighlighterRunner())
highlighter->setLanguageFeaturesFlags(document->languageFeatures().flags);
m_overviewModel.update(usesClangd() ? nullptr : document);

View File

@@ -268,10 +268,10 @@ void CppHighlighter::highlightBlock(const QString &text)
tokenize.expectedRawStringSuffix());
}
void CppHighlighter::setLanguageFeatures(const LanguageFeatures &languageFeatures)
void CppHighlighter::setLanguageFeaturesFlags(unsigned int flags)
{
if (languageFeatures != m_languageFeatures) {
m_languageFeatures = languageFeatures;
if (flags != m_languageFeatures.flags) {
m_languageFeatures.flags = flags;
rehighlight();
}
}

View File

@@ -22,7 +22,7 @@ class CPPEDITOR_EXPORT CppHighlighter : public TextEditor::SyntaxHighlighter
public:
CppHighlighter(QTextDocument *document = nullptr);
void setLanguageFeatures(const CPlusPlus::LanguageFeatures &languageFeatures);
void setLanguageFeaturesFlags(unsigned int flags) override;
void highlightBlock(const QString &text) override;
private:

View File

@@ -854,7 +854,7 @@ namespace Internal {
void decorateCppEditor(TextEditor::TextEditorWidget *editor)
{
editor->textDocument()->setSyntaxHighlighterCreator([] { return new CppHighlighter(); });
editor->textDocument()->resetSyntaxHighlighter([] { return new CppHighlighter(); });
editor->textDocument()->setIndenter(
new CppQtStyleIndenter(editor->textDocument()->document()));
editor->setAutoCompleter(new CppAutoCompleter);

View File

@@ -8,6 +8,7 @@
#include <texteditor/syntaxhighlighter.h>
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
#include <texteditor/syntaxhighlighterrunner.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
@@ -111,7 +112,7 @@ void SemanticHighlighter::handleHighlighterResults()
QElapsedTimer t;
t.start();
SyntaxHighlighter *highlighter = m_baseTextDocument->syntaxHighlighter();
BaseSyntaxHighlighterRunner *highlighter = m_baseTextDocument->syntaxHighlighterRunner();
QTC_ASSERT(highlighter, return);
incrementalApplyExtraAdditionalFormats(highlighter, m_watcher->future(), from, to, m_formatMap);
@@ -199,7 +200,7 @@ void SemanticHighlighter::onHighlighterFinished()
t.start();
if (!m_watcher->isCanceled() && documentRevision() == m_revision) {
SyntaxHighlighter *highlighter = m_baseTextDocument->syntaxHighlighter();
BaseSyntaxHighlighterRunner *highlighter = m_baseTextDocument->syntaxHighlighterRunner();
if (QTC_GUARD(highlighter)) {
qCDebug(log) << "onHighlighterFinished() - clearing formats";
clearExtraAdditionalFormatsUntilEnd(highlighter, m_watcher->future());

View File

@@ -93,7 +93,7 @@ DescriptionEditorWidget::DescriptionEditorWidget(QWidget *parent)
context->setContext(Context(Constants::C_DIFF_EDITOR_DESCRIPTION));
ICore::addContextObject(context);
textDocument()->setSyntaxHighlighterCreator([] { return new SyntaxHighlighter(); });
textDocument()->resetSyntaxHighlighter([] { return new SyntaxHighlighter(); });
}
QSize DescriptionEditorWidget::sizeHint() const

View File

@@ -246,10 +246,10 @@ void GitEditorWidget::init()
return;
const QChar commentChar = gitClient().commentChar(source());
if (isCommitEditor)
textDocument()->setSyntaxHighlighterCreator(
textDocument()->resetSyntaxHighlighter(
[commentChar] { return new GitSubmitHighlighter(commentChar); });
else if (isRebaseEditor)
textDocument()->setSyntaxHighlighterCreator(
textDocument()->resetSyntaxHighlighter(
[commentChar] { return new GitRebaseHighlighter(commentChar); });
}

View File

@@ -10,6 +10,7 @@
#include <texteditor/syntaxhighlighter.h>
#include <texteditor/texteditor.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/syntaxhighlighterrunner.h>
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
@@ -182,7 +183,7 @@ void SemanticTokenSupport::queueDocumentReload(TextEditor::TextDocument *doc)
void SemanticTokenSupport::clearHighlight(TextEditor::TextDocument *doc)
{
if (m_tokens.contains(doc->filePath())){
if (TextEditor::SyntaxHighlighter *highlighter = doc->syntaxHighlighter())
if (TextEditor::BaseSyntaxHighlighterRunner *highlighter = doc->syntaxHighlighterRunner())
highlighter->clearAllExtraFormats();
}
}
@@ -412,7 +413,7 @@ void SemanticTokenSupport::highlight(const Utils::FilePath &filePath, bool force
TextDocument *doc = TextDocument::textDocumentForFilePath(filePath);
if (!doc || LanguageClientManager::clientForDocument(doc) != m_client)
return;
SyntaxHighlighter *highlighter = doc->syntaxHighlighter();
BaseSyntaxHighlighterRunner *highlighter = doc->syntaxHighlighterRunner();
if (!highlighter)
return;
const VersionedTokens versionedTokens = m_tokens.value(filePath);

View File

@@ -51,7 +51,7 @@ NimEditorFactory::NimEditorFactory()
void NimEditorFactory::decorateEditor(TextEditorWidget *editor)
{
editor->textDocument()->setSyntaxHighlighterCreator([] { return new NimHighlighter();});
editor->textDocument()->resetSyntaxHighlighter([] { return new NimHighlighter();});
editor->textDocument()->setIndenter(new NimIndenter(editor->textDocument()->document()));
}

View File

@@ -167,7 +167,7 @@ BindingEditorFactory::BindingEditorFactory()
void BindingEditorFactory::decorateEditor(TextEditor::TextEditorWidget *editor)
{
editor->textDocument()->setSyntaxHighlighterCreator(
editor->textDocument()->resetSyntaxHighlighter(
[] { return new QmlJSEditor::QmlJSHighlighter(); });
editor->textDocument()->setIndenter(new QmlJSEditor::Internal::Indenter(
editor->textDocument()->document()));

View File

@@ -743,7 +743,7 @@ void QmlJSEditorWidget::inspectElementUnderCursor() const
widget->setReadOnly(true);
widget->textDocument()->setTemporary(true);
widget->textDocument()->setSyntaxHighlighterCreator([] {return new QmlJSHighlighter();});
widget->textDocument()->resetSyntaxHighlighter([] { return new QmlJSHighlighter(); });
const QString buf = inspectCppComponent(cppValue);
widget->textDocument()->setPlainText(buf);
@@ -1159,7 +1159,7 @@ QmlJSEditorFactory::QmlJSEditorFactory(Utils::Id _id)
void QmlJSEditorFactory::decorateEditor(TextEditorWidget *editor)
{
editor->textDocument()->setSyntaxHighlighterCreator([] { return new QmlJSHighlighter(); });
editor->textDocument()->resetSyntaxHighlighter([] { return new QmlJSHighlighter(); });
editor->textDocument()->setIndenter(new Internal::Indenter(editor->textDocument()->document()));
editor->setAutoCompleter(new AutoCompleter);
}

View File

@@ -820,7 +820,7 @@ QmlJSEditorDocument::QmlJSEditorDocument(Utils::Id id)
d, &Internal::QmlJSEditorDocumentPrivate::invalidateFormatterCache);
connect(this, &TextEditor::TextDocument::openFinishedSuccessfully,
d, &Internal::QmlJSEditorDocumentPrivate::settingsChanged);
setSyntaxHighlighterCreator([] { return new QmlJSHighlighter(); });
resetSyntaxHighlighter([] { return new QmlJSHighlighter(); });
setCodec(QTextCodec::codecForName("UTF-8")); // qml files are defined to be utf-8
setIndenter(new Internal::Indenter(document()));
}

View File

@@ -569,7 +569,7 @@ void SemanticHighlighter::applyResults(int from, int to)
if (m_enableHighlighting)
TextEditor::SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
m_document->syntaxHighlighter(), m_watcher.future(), from, to, m_extraFormats);
m_document->syntaxHighlighterRunner(), m_watcher.future(), from, to, m_extraFormats);
}
void SemanticHighlighter::finished()
@@ -584,7 +584,7 @@ void SemanticHighlighter::finished()
if (m_enableHighlighting)
TextEditor::SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(
m_document->syntaxHighlighter(), m_watcher.future());
m_document->syntaxHighlighterRunner(), m_watcher.future());
}
void SemanticHighlighter::run(QPromise<Use> &promise,

View File

@@ -96,6 +96,7 @@ add_qtc_plugin(TextEditor
snippets/snippetssettingspage.cpp snippets/snippetssettingspage.h
storagesettings.cpp storagesettings.h
syntaxhighlighter.cpp syntaxhighlighter.h
syntaxhighlighterrunner.cpp syntaxhighlighterrunner.h
tabsettings.cpp tabsettings.h
tabsettingswidget.cpp tabsettingswidget.h
textdocument.cpp textdocument.h

View File

@@ -74,6 +74,11 @@ Highlighter::Highlighter()
&categoryForTextStyle);
}
KSyntaxHighlighting::Definition Highlighter::getDefinition()
{
return definition();
}
static bool isOpeningParenthesis(QChar c)
{
return c == QLatin1Char('{') || c == QLatin1Char('[') || c == QLatin1Char('(');

View File

@@ -19,6 +19,8 @@ class Highlighter : public SyntaxHighlighter, public KSyntaxHighlighting::Abstra
public:
Highlighter();
KSyntaxHighlighting::Definition getDefinition() override;
protected:
void highlightBlock(const QString &text) override;
void applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) override;

View File

@@ -1,7 +1,7 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "syntaxhighlighter.h"
#include "syntaxhighlighterrunner.h"
#include "highlighter_test.h"
@@ -42,7 +42,7 @@ void GenerigHighlighterTests::initTestCase()
m_editor = qobject_cast<BaseTextEditor *>(editor);
m_editor->editorWidget()->configureGenericHighlighter(Utils::mimeTypeForName("application/json"));
QVERIFY(m_editor);
m_editor->textDocument()->syntaxHighlighter()->rehighlight();
m_editor->textDocument()->syntaxHighlighterRunner()->rehighlight();
}
using FormatRanges = QList<QTextLayout::FormatRange>;
@@ -190,8 +190,12 @@ void GenerigHighlighterTests::testPreeditText()
QTextBlock block = m_editor->textDocument()->document()->findBlockByNumber(2);
QVERIFY(block.isValid());
QTextCursor c(block);
c.beginEditBlock();
block.layout()->setPreeditArea(7, "uaf");
m_editor->textDocument()->syntaxHighlighter()->rehighlight();
c.endEditBlock();
m_editor->textDocument()->syntaxHighlighterRunner()->rehighlight();
const FormatRanges formatRanges = {{0, 4, toFormat(C_VISUAL_WHITESPACE)},
{4, 3, toFormat(C_TYPE)},

View File

@@ -9,6 +9,7 @@
#include "texteditor.h"
#include "texteditorsettings.h"
#include "texteditortr.h"
#include "syntaxhighlighterrunner.h"
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/icore.h>
@@ -209,7 +210,8 @@ void reload()
highlightRepository()->reload();
for (auto editor : Core::DocumentModel::editorsForOpenedDocuments()) {
if (auto textEditor = qobject_cast<BaseTextEditor *>(editor)) {
if (qobject_cast<Highlighter *>(textEditor->textDocument()->syntaxHighlighter()))
auto highlighterCreator = textEditor->textDocument()->syntaxHighlighterRunner()->creator();
if (highlighterCreator && qobject_cast<Highlighter *>(highlighterCreator()))
textEditor->editorWidget()->configureGenericHighlighter();
}
}

View File

@@ -5,6 +5,7 @@
#include "syntaxhighlighter.h"
#include "texteditorsettings.h"
#include "syntaxhighlighterrunner.h"
#include <utils/qtcassert.h>
@@ -73,9 +74,11 @@ const Ranges rangesForResult(
}
void SemanticHighlighter::incrementalApplyExtraAdditionalFormats(SyntaxHighlighter *highlighter,
void SemanticHighlighter::incrementalApplyExtraAdditionalFormats(
BaseSyntaxHighlighterRunner *highlighter,
const QFuture<HighlightingResult> &future,
int from, int to,
int from,
int to,
const QHash<int, QTextCharFormat> &kindToFormat,
const Splitter &splitter)
{
@@ -111,17 +114,21 @@ void SemanticHighlighter::incrementalApplyExtraAdditionalFormats(SyntaxHighlight
formatRanges[range.block].append(range.formatRange);
}
QList<int> clearBlockNumberVector;
QMap<int, QList<QTextLayout::FormatRange>> blockNumberMap;
for (auto &[block, ranges] : formatRanges) {
while (currentBlock < block) {
highlighter->clearExtraFormats(currentBlock);
clearBlockNumberVector.append(currentBlock.blockNumber());
currentBlock = currentBlock.next();
}
highlighter->setExtraFormats(block, std::move(ranges));
blockNumberMap[block.blockNumber()] = ranges;
currentBlock = block.next();
}
highlighter->clearExtraFormats(clearBlockNumberVector);
highlighter->setExtraFormats(blockNumberMap);
}
void SemanticHighlighter::setExtraAdditionalFormats(SyntaxHighlighter *highlighter,
void SemanticHighlighter::setExtraAdditionalFormats(BaseSyntaxHighlighterRunner *highlighter,
const QList<HighlightingResult> &results,
const QHash<int, QTextCharFormat> &kindToFormat)
{
@@ -132,20 +139,19 @@ void SemanticHighlighter::setExtraAdditionalFormats(SyntaxHighlighter *highlight
QTextDocument *doc = highlighter->document();
QTC_ASSERT(doc, return );
std::map<QTextBlock, QVector<QTextLayout::FormatRange>> formatRanges;
QMap<int, QList<QTextLayout::FormatRange>> blockNumberMap;
for (auto result : results) {
for (const Range &range : rangesForResult(result, doc, kindToFormat))
formatRanges[range.block].append(range.formatRange);
for (const HighlightingResult &result : results) {
const Ranges ranges = rangesForResult(result, doc, kindToFormat);
for (const Range &range : ranges)
blockNumberMap[range.block.blockNumber()].append(range.formatRange);
}
for (auto &[block, ranges] : formatRanges)
highlighter->setExtraFormats(block, std::move(ranges));
highlighter->setExtraFormats(blockNumberMap);
}
void SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(
SyntaxHighlighter *highlighter,
const QFuture<HighlightingResult> &future)
BaseSyntaxHighlighterRunner *highlighter, const QFuture<HighlightingResult> &future)
{
const QTextDocument * const doc = highlighter->document();
QTextBlock firstBlockToClear = doc->begin();
@@ -160,6 +166,9 @@ void SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd(
}
}
QList<int> clearBlockNumberVector;
for (QTextBlock b = firstBlockToClear; b.isValid(); b = b.next())
highlighter->clearExtraFormats(b);
clearBlockNumberVector.append(b.blockNumber());
highlighter->clearExtraFormats(clearBlockNumberVector);
}

View File

@@ -21,6 +21,7 @@ QT_END_NAMESPACE
namespace TextEditor {
class SyntaxHighlighter;
class BaseSyntaxHighlighterRunner;
class TEXTEDITOR_EXPORT HighlightingResult
{
@@ -72,10 +73,11 @@ using Splitter = std::function<const QList<std::pair<HighlightingResult, QTextBl
// from all lines that have no results between the (from-1).line result and
// the (to-1).line result.
// Requires that results of the Future are ordered by line.
void TEXTEDITOR_EXPORT incrementalApplyExtraAdditionalFormats(
SyntaxHighlighter *highlighter,
void TEXTEDITOR_EXPORT
incrementalApplyExtraAdditionalFormats(BaseSyntaxHighlighterRunner *highlighter,
const QFuture<HighlightingResult> &future,
int from, int to,
int from,
int to,
const QHash<int, QTextCharFormat> &kindToFormat,
const Splitter &splitter = {});
@@ -83,8 +85,7 @@ void TEXTEDITOR_EXPORT incrementalApplyExtraAdditionalFormats(
// indicated by Result::kind and kindToFormat to the correct location using
// SyntaxHighlighter::setExtraFormats. In contrast to
// incrementalApplyExtraAdditionalFormats the results do not have to be ordered by line.
void TEXTEDITOR_EXPORT setExtraAdditionalFormats(
SyntaxHighlighter *highlighter,
void TEXTEDITOR_EXPORT setExtraAdditionalFormats(BaseSyntaxHighlighterRunner *highlighter,
const HighlightingResults &results,
const QHash<int, QTextCharFormat> &kindToFormat);
@@ -92,9 +93,7 @@ void TEXTEDITOR_EXPORT setExtraAdditionalFormats(
// until the end of the document.
// Requires that results of the Future are ordered by line.
void TEXTEDITOR_EXPORT clearExtraAdditionalFormatsUntilEnd(
SyntaxHighlighter *highlighter,
const QFuture<HighlightingResult> &future);
BaseSyntaxHighlighterRunner *highlighter, const QFuture<HighlightingResult> &future);
} // namespace SemanticHighlighter
} // namespace TextEditor

View File

@@ -2,7 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "syntaxhighlighter.h"
#include "textdocument.h"
#include "textdocumentlayout.h"
#include "texteditorsettings.h"
#include "fontsettings.h"
@@ -54,13 +53,13 @@ public:
void updateFormats(const FontSettings &fontSettings);
FontSettings fontSettings;
QVector<QTextCharFormat> formatChanges;
QList<QTextCharFormat> formatChanges;
QTextBlock currentBlock;
bool rehighlightPending = false;
bool inReformatBlocks = false;
TextDocumentLayout::FoldValidator foldValidator;
QVector<QTextCharFormat> formats;
QVector<std::pair<int,TextStyle>> formatCategories;
QList<QTextCharFormat> formats;
QList<std::pair<int,TextStyle>> formatCategories;
QTextCharFormat whitespaceFormat;
bool noAutomaticHighlighting = false;
};
@@ -102,8 +101,8 @@ void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, in
QTextLayout *layout = currentBlock.layout();
QVector<QTextLayout::FormatRange> ranges;
QVector<QTextLayout::FormatRange> oldRanges;
QList<QTextLayout::FormatRange> ranges;
QList<QTextLayout::FormatRange> oldRanges;
std::tie(oldRanges, ranges)
= Utils::partition(layout->formats(), [](const QTextLayout::FormatRange &range) {
return range.format.property(SyntaxHighlight).toBool();
@@ -119,7 +118,7 @@ void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, in
QTextLayout::FormatRange r;
QVector<QTextLayout::FormatRange> newRanges;
QList<QTextLayout::FormatRange> newRanges;
int i = 0;
while (i < formatChanges.count()) {
@@ -179,6 +178,7 @@ void SyntaxHighlighter::reformatBlocks(int from, int charsRemoved, int charsAdde
void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int charsAdded)
{
Q_Q(SyntaxHighlighter);
foldValidator.reset();
rehighlightPending = false;
@@ -192,10 +192,12 @@ void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int ch
if (lastBlock.isValid())
endPosition = lastBlock.position() + lastBlock.length();
else
endPosition = doc->lastBlock().position() + doc->lastBlock().length(); //doc->docHandle()->length();
endPosition = doc->lastBlock().position() + doc->lastBlock().length();
bool forceHighlightOfNextBlock = false;
QList<SyntaxHighlighter::Result> vecRes;
while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) {
const int stateBeforeHighlight = block.userState();
@@ -203,12 +205,25 @@ void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int ch
forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight);
SyntaxHighlighter::Result res;
res.m_formatRanges = block.layout()->formats();
res.fillByBlock(block);
vecRes << res;
// Sending data to the text editor in chunks of 200 blocks is a sensible approach.
// This helps avoid UI slowdowns when applying formatting to the text.
if (vecRes.size() >= 200) {
emit q->resultsReady(vecRes);
vecRes.clear();
}
block = block.next();
}
formatChanges.clear();
foldValidator.finalize();
emit q->resultsReady(vecRes);
}
void SyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block, int from, int charsRemoved, int charsAdded)
@@ -670,22 +685,22 @@ static bool byStartOfRange(const QTextLayout::FormatRange &range, const QTextLay
return range.start < other.start;
}
// The formats is passed in by rvalue reference in order to prevent unnecessary copying of its items.
// After this function returns, the list is modified, and should be considered invalidated!
void SyntaxHighlighter::setExtraFormats(const QTextBlock &block,
QVector<QTextLayout::FormatRange> &&formats)
const QList<QTextLayout::FormatRange> &formats)
{
Q_D(SyntaxHighlighter);
QList<QTextLayout::FormatRange> formatsCopy = formats;
const int blockLength = block.length();
if (block.layout() == nullptr || blockLength == 0)
return;
const QString preeditText = block.layout()->preeditAreaText();
if (!preeditText.isEmpty()) {
QVector<QTextLayout::FormatRange> additionalRanges;
QList<QTextLayout::FormatRange> additionalRanges;
const int preeditPosition = block.layout()->preeditAreaPosition();
for (QTextLayout::FormatRange &r : formats) {
for (QTextLayout::FormatRange &r : formatsCopy) {
if (r.start >= preeditPosition) {
r.start += preeditText.length();
} else if (r.start + r.length > preeditPosition) {
@@ -696,33 +711,39 @@ void SyntaxHighlighter::setExtraFormats(const QTextBlock &block,
r.length = preeditPosition - r.start;
}
}
formats << additionalRanges;
formatsCopy << additionalRanges;
}
Utils::sort(formats, byStartOfRange);
Utils::sort(formatsCopy, byStartOfRange);
const QVector<QTextLayout::FormatRange> all = block.layout()->formats();
QVector<QTextLayout::FormatRange> previousSemanticFormats;
QVector<QTextLayout::FormatRange> formatsToApply;
const QList<QTextLayout::FormatRange> all = block.layout()->formats();
QList<QTextLayout::FormatRange> previousSemanticFormats;
QList<QTextLayout::FormatRange> formatsToApply;
std::tie(previousSemanticFormats, formatsToApply)
= Utils::partition(all, [](const QTextLayout::FormatRange &r) {
return r.format.property(SemanticHighlight).toBool();
});
for (auto &format : formats)
for (auto &format : formatsCopy)
format.format.setProperty(SemanticHighlight, true);
if (formats.size() == previousSemanticFormats.size()) {
if (formatsCopy.size() == previousSemanticFormats.size()) {
Utils::sort(previousSemanticFormats, byStartOfRange);
if (formats == previousSemanticFormats)
return;
}
formatsToApply += formats;
formatsToApply += formatsCopy;
bool wasInReformatBlocks = d->inReformatBlocks;
d->inReformatBlocks = true;
block.layout()->setFormats(formatsToApply);
SyntaxHighlighter::Result res;
res.m_formatRanges = block.layout()->formats();
res.fillByBlock(block);
emit resultsReady({res});
document()->markContentsDirty(block.position(), blockLength - 1);
d->inReformatBlocks = wasInReformatBlocks;
}
@@ -735,7 +756,7 @@ void SyntaxHighlighter::clearExtraFormats(const QTextBlock &block)
if (block.layout() == nullptr || blockLength == 0)
return;
const QVector<QTextLayout::FormatRange> formatsToApply
const QList<QTextLayout::FormatRange> formatsToApply
= Utils::filtered(block.layout()->formats(), [](const QTextLayout::FormatRange &r) {
return !r.format.property(SemanticHighlight).toBool();
});
@@ -743,6 +764,12 @@ void SyntaxHighlighter::clearExtraFormats(const QTextBlock &block)
bool wasInReformatBlocks = d->inReformatBlocks;
d->inReformatBlocks = true;
block.layout()->setFormats(formatsToApply);
SyntaxHighlighter::Result res;
res.m_formatRanges = block.layout()->formats();
res.fillByBlock(block);
emit resultsReady({res});
document()->markContentsDirty(block.position(), blockLength - 1);
d->inReformatBlocks = wasInReformatBlocks;
}
@@ -832,7 +859,7 @@ void SyntaxHighlighter::setDefaultTextFormatCategories()
void SyntaxHighlighter::setTextFormatCategories(int count,
std::function<TextStyle(int)> formatMapping)
{
QVector<std::pair<int, TextStyle>> categories;
QList<std::pair<int, TextStyle>> categories;
categories.reserve(count);
for (int i = 0; i < count; ++i)
categories.append({i, formatMapping(i)});
@@ -848,12 +875,12 @@ void SyntaxHighlighter::setTextFormatCategories(int count,
\sa setDefaultTextFormatCategories()
*/
void SyntaxHighlighter::setTextFormatCategories(const QVector<std::pair<int, TextStyle>> &categories)
void SyntaxHighlighter::setTextFormatCategories(const QList<std::pair<int, TextStyle>> &categories)
{
Q_D(SyntaxHighlighter);
d->formatCategories = categories;
const int maxCategory = Utils::maxElementOr(categories, {-1, C_TEXT}).first;
d->formats = QVector<QTextCharFormat>(maxCategory + 1);
d->formats = QList<QTextCharFormat>(maxCategory + 1);
d->updateFormats(TextEditorSettings::fontSettings());
}
@@ -899,4 +926,3 @@ void SyntaxHighlighterPrivate::updateFormats(const FontSettings &fontSettings)
} // namespace TextEditor
#include "moc_syntaxhighlighter.cpp"

View File

@@ -6,6 +6,9 @@
#include "texteditor_global.h"
#include <texteditor/texteditorconstants.h>
#include <texteditor/textdocumentlayout.h>
#include <KSyntaxHighlighting/Definition>
#include <QObject>
#include <QTextLayout>
@@ -41,10 +44,6 @@ public:
void setDocument(QTextDocument *doc);
QTextDocument *document() const;
void setExtraFormats(const QTextBlock &block, QVector<QTextLayout::FormatRange> &&formats);
void clearExtraFormats(const QTextBlock &block);
void clearAllExtraFormats();
static QList<QColor> generateColors(int n, const QColor &background);
// Don't call in constructors of derived classes
@@ -53,9 +52,73 @@ public:
void setNoAutomaticHighlighting(bool noAutomatic);
struct Result
{
void fillByBlock(const QTextBlock &block)
{
m_blockNumber = block.position();
m_userState = block.userState();
TextBlockUserData *userDate = TextDocumentLayout::textUserData(block);
if (!userDate)
return;
m_hasBlockUserData = true;
m_foldingIndent = userDate->foldingIndent();
m_folded = userDate->folded();
m_ifdefedOut = userDate->ifdefedOut();
m_foldingStartIncluded = userDate->foldingStartIncluded();
m_foldingEndIncluded = userDate->foldingEndIncluded();
m_parentheses = userDate->parentheses();
m_expectedRawStringSuffix = userDate->expectedRawStringSuffix();
}
void copyToBlock(QTextBlock &block) const
{
block.setUserState(m_userState);
if (m_hasBlockUserData) {
TextBlockUserData *data = TextDocumentLayout::userData(block);
data->setExpectedRawStringSuffix(m_expectedRawStringSuffix);
data->setFolded(m_folded);
data->setFoldingIndent(m_foldingIndent);
data->setFoldingStartIncluded(m_foldingStartIncluded);
data->setFoldingEndIncluded(m_foldingEndIncluded);
if (m_ifdefedOut)
data->setIfdefedOut();
else
data->clearIfdefedOut();
data->setParentheses(m_parentheses);
}
}
int m_blockNumber;
bool m_hasBlockUserData = false;
int m_foldingIndent : 16;
uint m_folded : 1;
uint m_ifdefedOut : 1;
uint m_foldingStartIncluded : 1;
uint m_foldingEndIncluded : 1;
Parentheses m_parentheses;
QByteArray m_expectedRawStringSuffix;
int m_userState = -1;
QList<QTextLayout::FormatRange> m_formatRanges;
};
void setExtraFormats(const QTextBlock &block, const QList<QTextLayout::FormatRange> &formats);
virtual void setLanguageFeaturesFlags(unsigned int /*flags*/) {}; // needed for CppHighlighting
virtual void setEnabled(bool /*enabled*/) {}; // needed for DiffAndLogHighlighter
virtual KSyntaxHighlighting::Definition getDefinition() { return {}; }
public slots:
virtual void rehighlight();
void rehighlightBlock(const QTextBlock &block);
void clearExtraFormats(const QTextBlock &block);
void clearAllExtraFormats();
protected:
void setDefaultTextFormatCategories();
@@ -89,8 +152,11 @@ protected:
protected:
virtual void documentChanged(QTextDocument * /*oldDoc*/, QTextDocument * /*newDoc*/) {};
signals:
void resultsReady(const QList<Result> &result);
private:
void setTextFormatCategories(const QVector<std::pair<int, TextStyle>> &categories);
void setTextFormatCategories(const QList<std::pair<int, TextStyle>> &categories);
void reformatBlocks(int from, int charsRemoved, int charsAdded);
void delayedRehighlight();

View File

@@ -0,0 +1,255 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "syntaxhighlighterrunner.h"
#include <texteditor/fontsettings.h>
#include <texteditor/textdocumentlayout.h>
#include <utils/textutils.h>
#include <QMetaObject>
#include <QTextCursor>
#include <QTextDocument>
#include <QThread>
namespace TextEditor {
class SyntaxHighlighterRunnerPrivate : public QObject
{
Q_OBJECT
public:
SyntaxHighlighterRunnerPrivate(BaseSyntaxHighlighterRunner::SyntaxHighLighterCreator creator,
QTextDocument *document)
: m_creator(creator)
{
m_highlighter.reset(m_creator());
m_highlighter->setDocument(document);
m_document = document;
}
void create()
{
if (m_document != nullptr)
return;
m_document = new QTextDocument(this);
m_document->setDocumentLayout(new TextDocumentLayout(m_document));
m_highlighter.reset(m_creator());
m_highlighter->setDocument(m_document);
connect(m_highlighter.get(),
&SyntaxHighlighter::resultsReady,
this,
&SyntaxHighlighterRunnerPrivate::resultsReady);
}
void cloneDocument(int from,
int charsRemoved,
const QString textAdded,
const QMap<int, BaseSyntaxHighlighterRunner::BlockPreeditData> &blocksPreedit)
{
QTextCursor cursor(m_document);
cursor.setPosition(qMin(m_document->characterCount() - 1, from + charsRemoved));
cursor.setPosition(from, QTextCursor::KeepAnchor);
cursor.insertText(textAdded);
for (auto it = blocksPreedit.cbegin(); it != blocksPreedit.cend(); ++it) {
const QTextBlock block = m_document->findBlock(it.key());
block.layout()->setPreeditArea(it.value().position, it.value().text);
}
}
void setExtraFormats(const QMap<int, QList<QTextLayout::FormatRange>> &formatMap)
{
for (auto it = formatMap.cbegin(); it != formatMap.cend(); ++it)
m_highlighter->setExtraFormats(m_document->findBlockByNumber(it.key()), it.value());
}
void clearExtraFormats(const QList<int> &blockNumbers)
{
for (auto it = blockNumbers.cbegin(); it != blockNumbers.cend(); ++it)
m_highlighter->clearExtraFormats(m_document->findBlockByNumber(*it));
}
void clearAllExtraFormats() { m_highlighter->clearAllExtraFormats(); }
void setFontSettings(const TextEditor::FontSettings &fontSettings)
{
m_highlighter->setFontSettings(fontSettings);
m_highlighter->rehighlight();
}
KSyntaxHighlighting::Definition getDefinition() { return m_highlighter->getDefinition(); }
void setLanguageFeaturesFlags(unsigned int flags)
{
m_highlighter->setLanguageFeaturesFlags(flags);
}
void setEnabled(bool enabled) { m_highlighter->setEnabled(enabled); }
void rehighlight() { m_highlighter->rehighlight(); }
signals:
void resultsReady(const QList<SyntaxHighlighter::Result> &result);
private:
BaseSyntaxHighlighterRunner::SyntaxHighLighterCreator m_creator;
std::unique_ptr<SyntaxHighlighter> m_highlighter;
QTextDocument *m_document = nullptr;
};
// ----------------------------- BaseSyntaxHighlighterRunner --------------------------------------
BaseSyntaxHighlighterRunner::BaseSyntaxHighlighterRunner(
BaseSyntaxHighlighterRunner::SyntaxHighLighterCreator creator, QTextDocument *document)
: d(new SyntaxHighlighterRunnerPrivate(creator, document))
{
m_document = document;
}
BaseSyntaxHighlighterRunner::~BaseSyntaxHighlighterRunner() = default;
void BaseSyntaxHighlighterRunner::applyFormatRanges(const SyntaxHighlighter::Result &result)
{
QTextBlock docBlock = m_document->findBlock(result.m_blockNumber);
if (!docBlock.isValid())
return;
result.copyToBlock(docBlock);
if (!result.m_formatRanges.empty()) {
TextDocumentLayout::FoldValidator foldValidator;
foldValidator.setup(qobject_cast<TextDocumentLayout *>(m_document->documentLayout()));
docBlock.layout()->setFormats(result.m_formatRanges);
m_document->markContentsDirty(docBlock.position(), docBlock.length());
foldValidator.process(docBlock);
}
}
void BaseSyntaxHighlighterRunner::cloneDocumentData(int from, int charsRemoved, int charsAdded)
{
QMap<int, BaseSyntaxHighlighterRunner::BlockPreeditData> blocksPreedit;
QTextBlock firstBlock = m_document->findBlock(from);
QTextBlock endBlock = m_document->findBlock(from + charsAdded);
while (firstBlock.isValid() && firstBlock.position() < endBlock.position()) {
const int position = firstBlock.position();
if (firstBlock.layout()) {
const int preeditAreaPosition = firstBlock.layout()->preeditAreaPosition();
const QString preeditAreaText = firstBlock.layout()->preeditAreaText();
if (preeditAreaPosition != -1)
blocksPreedit[position] = {preeditAreaPosition, preeditAreaText};
}
firstBlock = firstBlock.next();
}
const QString text = Utils::Text::textAt(QTextCursor(m_document), from, charsAdded);
cloneDocument(from, charsRemoved, text, blocksPreedit);
}
void BaseSyntaxHighlighterRunner::cloneDocument(int from,
int charsRemoved,
const QString textAdded,
const QMap<int, BlockPreeditData> &blocksPreedit)
{
QMetaObject::invokeMethod(d.get(), [this, from, charsRemoved, textAdded, blocksPreedit] {
d->cloneDocument(from, charsRemoved, textAdded, blocksPreedit);
});
}
void BaseSyntaxHighlighterRunner::setExtraFormats(
const QMap<int, QList<QTextLayout::FormatRange>> &formatMap)
{
QMetaObject::invokeMethod(d.get(), [this, formatMap] { d->setExtraFormats(formatMap); });
}
void BaseSyntaxHighlighterRunner::clearExtraFormats(const QList<int> &blockNumbers)
{
QMetaObject::invokeMethod(d.get(), [this, blockNumbers] { d->clearExtraFormats(blockNumbers); });
}
void BaseSyntaxHighlighterRunner::clearAllExtraFormats()
{
QMetaObject::invokeMethod(d.get(), [this] { d->clearAllExtraFormats(); });
}
void BaseSyntaxHighlighterRunner::setFontSettings(const TextEditor::FontSettings &fontSettings)
{
QMetaObject::invokeMethod(d.get(), [this, fontSettings] { d->setFontSettings(fontSettings); });
}
void BaseSyntaxHighlighterRunner::setLanguageFeaturesFlags(unsigned int flags)
{
QMetaObject::invokeMethod(d.get(), [this, flags] { d->setLanguageFeaturesFlags(flags); });
}
void BaseSyntaxHighlighterRunner::setEnabled(bool enabled)
{
QMetaObject::invokeMethod(d.get(), [this, enabled] { d->setEnabled(enabled); });
}
void BaseSyntaxHighlighterRunner::rehighlight()
{
QMetaObject::invokeMethod(d.get(), [this] { d->rehighlight(); });
}
KSyntaxHighlighting::Definition BaseSyntaxHighlighterRunner::getDefinition()
{
return d->getDefinition();
}
// --------------------------- ThreadedSyntaxHighlighterRunner ------------------------------------
ThreadedSyntaxHighlighterRunner::ThreadedSyntaxHighlighterRunner(SyntaxHighLighterCreator creator,
QTextDocument *document)
: BaseSyntaxHighlighterRunner(creator, nullptr)
{
QTC_ASSERT(document, return);
d->moveToThread(&m_thread);
connect(&m_thread, &QThread::finished, d.get(), [this] { d.release()->deleteLater(); });
m_thread.start();
QMetaObject::invokeMethod(d.get(), &SyntaxHighlighterRunnerPrivate::create);
m_document = document;
connect(d.get(),
&SyntaxHighlighterRunnerPrivate::resultsReady,
document,
[this](const QList<SyntaxHighlighter::Result> &result) {
for (const SyntaxHighlighter::Result &res : result)
applyFormatRanges(res);
});
cloneDocumentData(0, 0, document->characterCount());
connect(document,
&QTextDocument::contentsChange,
this,
[this](int from, int charsRemoved, int charsAdded) {
if (!this->document())
return;
cloneDocumentData(from, charsRemoved, charsAdded);
});
}
ThreadedSyntaxHighlighterRunner::~ThreadedSyntaxHighlighterRunner()
{
m_thread.quit();
m_thread.wait();
}
KSyntaxHighlighting::Definition ThreadedSyntaxHighlighterRunner::getDefinition()
{
KSyntaxHighlighting::Definition definition;
QMetaObject::invokeMethod(
d.get(),
[this, &definition] { definition = d->getDefinition(); },
Qt::BlockingQueuedConnection);
return definition;
}
} // namespace TextEditor
#include "syntaxhighlighterrunner.moc"

View File

@@ -0,0 +1,75 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <texteditor/syntaxhighlighter.h>
#include <KSyntaxHighlighting/Definition>
#include <QObject>
#include <QPointer>
QT_BEGIN_NAMESPACE
class QTextDocument;
QT_END_NAMESPACE
namespace TextEditor {
class SyntaxHighlighterRunnerPrivate;
class TEXTEDITOR_EXPORT BaseSyntaxHighlighterRunner : public QObject
{
Q_OBJECT
public:
using SyntaxHighLighterCreator = std::function<SyntaxHighlighter *()>;
struct BlockPreeditData {
int position;
QString text;
};
BaseSyntaxHighlighterRunner(SyntaxHighLighterCreator creator, QTextDocument *document);
virtual ~BaseSyntaxHighlighterRunner();
void setExtraFormats(const QMap<int, QList<QTextLayout::FormatRange>> &formats);
void clearExtraFormats(const QList<int> &blockNumbers);
void clearAllExtraFormats();
void setFontSettings(const TextEditor::FontSettings &fontSettings);
void setLanguageFeaturesFlags(unsigned int flags);
void setEnabled(bool enabled);
void rehighlight();
virtual KSyntaxHighlighting::Definition getDefinition();
QTextDocument *document() const { return m_document; }
SyntaxHighLighterCreator creator() const { return m_creator; }
protected:
std::unique_ptr<SyntaxHighlighterRunnerPrivate> d;
QPointer<QTextDocument> m_document = nullptr;
void applyFormatRanges(const SyntaxHighlighter::Result &result);
void cloneDocumentData(int from, int charsRemoved, int charsAdded);
void cloneDocument(int from,
int charsRemoved,
const QString textAdded,
const QMap<int, BlockPreeditData> &blocksPreedit);
private:
SyntaxHighLighterCreator m_creator;
};
class TEXTEDITOR_EXPORT ThreadedSyntaxHighlighterRunner : public BaseSyntaxHighlighterRunner
{
public:
ThreadedSyntaxHighlighterRunner(SyntaxHighLighterCreator SyntaxHighLighterCreator,
QTextDocument *document);
~ThreadedSyntaxHighlighterRunner();
KSyntaxHighlighting::Definition getDefinition() override;
private:
QThread m_thread;
};
} // namespace TextEditor

View File

@@ -13,11 +13,13 @@
#include "texteditortr.h"
#include "textindenter.h"
#include "typingsettings.h"
#include "syntaxhighlighterrunner.h"
#include <coreplugin/coreconstants.h>
#include <coreplugin/diffservice.h>
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
@@ -73,7 +75,6 @@ public:
FontSettings m_fontSettings;
bool m_fontSettingsNeedsApply = false; // for applying font settings delayed till an editor becomes visible
QTextDocument m_document;
SyntaxHighlighter *m_highlighter = nullptr;
CompletionAssistProvider *m_completionAssistProvider = nullptr;
CompletionAssistProvider *m_functionHintAssistProvider = nullptr;
IAssistProvider *m_quickFixProvider = nullptr;
@@ -92,6 +93,8 @@ public:
TextMarks m_marksCache; // Marks not owned
Utils::Guard m_modificationChangedGuard;
BaseSyntaxHighlighterRunner *m_highlighterRunner = nullptr;
};
MultiTextCursor TextDocumentPrivate::indentOrUnindent(const MultiTextCursor &cursors,
@@ -450,10 +453,8 @@ void TextDocument::applyFontSettings()
block = block.next();
}
updateLayout();
if (d->m_highlighter) {
d->m_highlighter->setFontSettings(d->m_fontSettings);
d->m_highlighter->rehighlight();
}
if (d->m_highlighterRunner)
d->m_highlighterRunner->setFontSettings(d->m_fontSettings);
}
const FontSettings &TextDocument::fontSettings() const
@@ -625,9 +626,9 @@ QTextDocument *TextDocument::document() const
return &d->m_document;
}
SyntaxHighlighter *TextDocument::syntaxHighlighter() const
BaseSyntaxHighlighterRunner *TextDocument::syntaxHighlighterRunner() const
{
return d->m_highlighter;
return d->m_highlighterRunner;
}
/*!
@@ -901,14 +902,18 @@ bool TextDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type
return reload(errorString);
}
void TextDocument::setSyntaxHighlighterCreator(const SyntaxHighLighterCreator &creator)
void TextDocument::resetSyntaxHighlighter(const std::function<SyntaxHighlighter *()> &creator,
bool threaded)
{
if (d->m_highlighter)
delete d->m_highlighter;
if (d->m_highlighterRunner)
delete d->m_highlighterRunner;
d->m_highlighter = creator();
d->m_highlighter->setParent(this);
d->m_highlighter->setDocument(&d->m_document);
if (threaded) {
d->m_highlighterRunner = new ThreadedSyntaxHighlighterRunner(creator, document());
return;
}
d->m_highlighterRunner = new BaseSyntaxHighlighterRunner(creator, document());
}
void TextDocument::cleanWhitespace(const QTextCursor &cursor)

View File

@@ -34,6 +34,7 @@ class FontSettings;
class IAssistProvider;
class StorageSettings;
class SyntaxHighlighter;
class BaseSyntaxHighlighterRunner;
class TabSettings;
class TextDocumentPrivate;
class TextMark;
@@ -126,8 +127,8 @@ public:
QTextDocument *document() const;
using SyntaxHighLighterCreator = std::function<SyntaxHighlighter *()>;
void setSyntaxHighlighterCreator(const SyntaxHighLighterCreator &creator);
SyntaxHighlighter *syntaxHighlighter() const;
void resetSyntaxHighlighter(const SyntaxHighLighterCreator &creator, bool threaded = true);
BaseSyntaxHighlighterRunner *syntaxHighlighterRunner() const;
bool reload(QString *errorString, QTextCodec *codec);
void cleanWhitespace(const QTextCursor &cursor);

View File

@@ -34,6 +34,7 @@
#include "texteditorsettings.h"
#include "texteditortr.h"
#include "typingsettings.h"
#include "syntaxhighlighterrunner.h"
#include <aggregation/aggregate.h>
@@ -1930,9 +1931,11 @@ void TextEditorWidgetPrivate::foldLicenseHeader()
const QString trimmedText = text.trimmed();
QStringList commentMarker;
QStringList docMarker;
if (auto highlighter = qobject_cast<Highlighter *>(
q->textDocument()->syntaxHighlighter())) {
const HighlighterHelper::Definition def = highlighter->definition();
HighlighterHelper::Definition def;
if (BaseSyntaxHighlighterRunner *highlighter = q->textDocument()->syntaxHighlighterRunner())
def = highlighter->getDefinition();
if (def.isValid()) {
for (const QString &marker :
{def.singleLineCommentMarker(), def.multiLineCommentMarker().first}) {
if (!marker.isEmpty())
@@ -3706,12 +3709,12 @@ void TextEditorWidgetPrivate::configureGenericHighlighter(
q->setCodeFoldingSupported(false);
}
m_document->setSyntaxHighlighterCreator([definition] {
m_document->resetSyntaxHighlighter([definition] {
auto highlighter = new Highlighter();
if (definition.isValid())
highlighter->setDefinition(definition);
return highlighter;
});
}, false);
m_document->setFontSettings(TextEditorSettings::fontSettings());
}
@@ -3736,8 +3739,8 @@ void TextEditorWidgetPrivate::setupFromDefinition(const KSyntaxHighlighting::Def
KSyntaxHighlighting::Definition TextEditorWidgetPrivate::currentDefinition()
{
if (auto highlighter = qobject_cast<Highlighter *>(m_document->syntaxHighlighter()))
return highlighter->definition();
if (BaseSyntaxHighlighterRunner *highlighter = m_document->syntaxHighlighterRunner())
return highlighter->getDefinition();
return {};
}
@@ -8120,7 +8123,7 @@ void TextEditorWidget::setDisplaySettings(const DisplaySettings &ds)
optionFlags.setFlag(QTextOption::AddSpaceForLineAndParagraphSeparators);
optionFlags.setFlag(QTextOption::ShowTabsAndSpaces, ds.m_visualizeWhitespace);
if (optionFlags != currentOptionFlags) {
if (SyntaxHighlighter *highlighter = textDocument()->syntaxHighlighter())
if (BaseSyntaxHighlighterRunner *highlighter = textDocument()->syntaxHighlighterRunner())
highlighter->rehighlight();
QTextOption option = document()->defaultTextOption();
option.setFlags(optionFlags);
@@ -9259,7 +9262,7 @@ QString TextEditorWidget::textAt(int from, int to) const
void TextEditorWidget::configureGenericHighlighter()
{
HighlighterHelper::Definitions definitions = HighlighterHelper::definitionsForDocument(textDocument());
const HighlighterHelper::Definitions definitions = HighlighterHelper::definitionsForDocument(textDocument());
d->configureGenericHighlighter(definitions.isEmpty() ? HighlighterHelper::Definition()
: definitions.first());
d->updateSyntaxInfoBar(definitions, textDocument()->filePath().fileName());
@@ -9267,7 +9270,7 @@ void TextEditorWidget::configureGenericHighlighter()
void TextEditorWidget::configureGenericHighlighter(const Utils::MimeType &mimeType)
{
HighlighterHelper::Definitions definitions = HighlighterHelper::definitionsForMimeType(mimeType.name());
const HighlighterHelper::Definitions definitions = HighlighterHelper::definitionsForMimeType(mimeType.name());
d->configureGenericHighlighter(definitions.isEmpty() ? HighlighterHelper::Definition()
: definitions.first());
d->removeSyntaxInfoBar();
@@ -9275,7 +9278,7 @@ void TextEditorWidget::configureGenericHighlighter(const Utils::MimeType &mimeTy
expected_str<void> TextEditorWidget::configureGenericHighlighter(const QString &definitionName)
{
HighlighterHelper::Definition definition = TextEditor::HighlighterHelper::definitionForName(definitionName);
const HighlighterHelper::Definition definition = TextEditor::HighlighterHelper::definitionForName(definitionName);
if (!definition.isValid())
return make_unexpected(Tr::tr("Could not find definition."));
@@ -9453,7 +9456,7 @@ void TextEditorFactory::setEditorCreator(const EditorCreator &creator)
doc->setIndenter(d->m_indenterCreator(doc->document()));
if (d->m_syntaxHighlighterCreator)
doc->setSyntaxHighlighterCreator(d->m_syntaxHighlighterCreator);
doc->resetSyntaxHighlighter(d->m_syntaxHighlighterCreator);
doc->setCompletionAssistProvider(d->m_completionAssistProvider ? d->m_completionAssistProvider
: &basicSnippetProvider);

View File

@@ -124,6 +124,8 @@ QtcPlugin {
"storagesettings.h",
"syntaxhighlighter.cpp",
"syntaxhighlighter.h",
"syntaxhighlighterrunner.cpp",
"syntaxhighlighterrunner.h",
"tabsettings.cpp",
"tabsettings.h",
"tabsettingswidget.cpp",

View File

@@ -222,9 +222,9 @@ void DiffAndLogHighlighter::setFontSettings(const TextEditor::FontSettings &font
d->updateOtherFormats();
}
void DiffAndLogHighlighter::setEnabled(bool e)
void DiffAndLogHighlighter::setEnabled(bool enabled)
{
d->m_enabled = e;
d->m_enabled = enabled;
}
} // namespace VcsBase

View File

@@ -23,11 +23,11 @@ public:
~DiffAndLogHighlighter() override;
void highlightBlock(const QString &text) override;
void setEnabled(bool enabled) override;
protected:
void setFontSettings(const TextEditor::FontSettings &fontSettings) override;
void setEnabled(bool e);
private:
friend class DiffAndLogHighlighterPrivate;
DiffAndLogHighlighterPrivate *const d;

View File

@@ -28,6 +28,7 @@
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
#include <texteditor/syntaxhighlighterrunner.h>
#include <utils/algorithm.h>
#include <utils/progressindicator.h>
@@ -761,7 +762,7 @@ void VcsBaseEditorWidget::init()
}
if (hasDiff()) {
setCodeFoldingSupported(true);
textDocument()->setSyntaxHighlighterCreator(
textDocument()->resetSyntaxHighlighter(
[diffFilePattern = d->m_diffFilePattern, logEntryPattern = d->m_logEntryPattern] {
return new DiffAndLogHighlighter(diffFilePattern, logEntryPattern);
});
@@ -824,8 +825,7 @@ void VcsBaseEditorWidget::setFileLogAnnotateEnabled(bool e)
void VcsBaseEditorWidget::setHighlightingEnabled(bool e)
{
auto dh = static_cast<DiffAndLogHighlighter *>(textDocument()->syntaxHighlighter());
dh->setEnabled(e);
textDocument()->syntaxHighlighterRunner()->setEnabled(e);
}
FilePath VcsBaseEditorWidget::workingDirectory() const
@@ -1100,11 +1100,11 @@ void VcsBaseEditorWidget::slotActivateAnnotation()
disconnect(this, &QPlainTextEdit::textChanged, this, &VcsBaseEditorWidget::slotActivateAnnotation);
if (auto ah = qobject_cast<BaseAnnotationHighlighter *>(textDocument()->syntaxHighlighter())) {
if (BaseSyntaxHighlighterRunner *ah = textDocument()->syntaxHighlighterRunner()) {
ah->rehighlight();
} else {
BaseAnnotationHighlighterCreator creator = annotationHighlighterCreator();
textDocument()->setSyntaxHighlighterCreator(
textDocument()->resetSyntaxHighlighter(
[creator, annotation = d->m_annotation] { return creator(annotation); });
}
}

View File

@@ -7,6 +7,7 @@
#include <texteditor/semantichighlighter.h>
#include <texteditor/syntaxhighlighter.h>
#include <texteditor/syntaxhighlighterrunner.h>
#include <texteditor/texteditorsettings.h>
#include <QObject>
@@ -31,7 +32,7 @@ private slots:
private:
QTextDocument *doc = nullptr;
SyntaxHighlighter *highlighter = nullptr;
BaseSyntaxHighlighterRunner *highlighterRunner = nullptr;
FontSettings fontsettings;
QHash<int, QTextCharFormat> formatHash;
QTextCharFormat whitespaceFormat;
@@ -59,7 +60,9 @@ Last)";
doc = new QTextDocument();
doc->setPlainText(text);
highlighter = new SyntaxHighlighter(doc, fontsettings);
highlighterRunner
= new BaseSyntaxHighlighterRunner([this] { return new SyntaxHighlighter(doc, fontsettings); },
doc);
}
static const HighlightingResults &highlightingResults()
@@ -77,7 +80,7 @@ void tst_highlighter::test_setExtraAdditionalFormats()
{
QCOMPARE(doc->blockCount(), 4);
SemanticHighlighter::setExtraAdditionalFormats(highlighter, highlightingResults(), formatHash);
SemanticHighlighter::setExtraAdditionalFormats(highlighterRunner, highlightingResults(), formatHash);
for (auto block = doc->firstBlock(); block.isValid(); block = block.next()) {
QVERIFY(block.blockNumber() < 4);
@@ -140,17 +143,17 @@ void tst_highlighter::test_clearExtraFormats()
{
QCOMPARE(doc->blockCount(), 4);
SemanticHighlighter::setExtraAdditionalFormats(highlighter, highlightingResults(), formatHash);
SemanticHighlighter::setExtraAdditionalFormats(highlighterRunner, highlightingResults(), formatHash);
QTextBlock firstBlock = doc->findBlockByNumber(0);
QTextBlock spacesLineBlock = doc->findBlockByNumber(1);
QTextBlock emptyLineBlock = doc->findBlockByNumber(2);
QTextBlock lastBlock = doc->findBlockByNumber(3);
highlighter->clearExtraFormats(emptyLineBlock);
highlighterRunner->clearExtraFormats({emptyLineBlock.blockNumber()});
QVERIFY(emptyLineBlock.layout()->formats().isEmpty());
highlighter->clearExtraFormats(spacesLineBlock);
highlighterRunner->clearExtraFormats({spacesLineBlock.blockNumber()});
auto formats = spacesLineBlock.layout()->formats();
// the spaces are not extra formats and should remain when clearing extra formats
@@ -185,7 +188,7 @@ void tst_highlighter::test_clearExtraFormats()
QCOMPARE(formats.at(1).start, 0);
QCOMPARE(formats.at(1).length, 5);
highlighter->clearAllExtraFormats();
highlighterRunner->clearAllExtraFormats();
QVERIFY(firstBlock.layout()->formats().isEmpty());
QVERIFY(emptyLineBlock.layout()->formats().isEmpty());
@@ -221,7 +224,7 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
QFutureInterface<HighlightingResult> fiOld;
fiOld.reportResults(highlightingResults());
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighterRunner,
fiOld.future(),
2,
0,
@@ -229,7 +232,7 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
auto formats = firstBlock.layout()->formats();
QVERIFY(formats.isEmpty());
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighterRunner,
fiOld.future(),
0,
2,
@@ -254,7 +257,7 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
QCOMPARE(formats.at(1).start, 11);
QCOMPARE(formats.at(1).length, 1);
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighterRunner,
fiOld.future(),
3,
6,
@@ -280,7 +283,7 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
QFutureInterface<HighlightingResult> fiNew;
fiNew.reportResults(newResults);
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighterRunner,
fiNew.future(),
0,
3,
@@ -294,7 +297,7 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
QCOMPARE(formats.at(0).start, 0);
QCOMPARE(formats.at(0).length, 1);
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighterRunner,
fiNew.future(),
3,
4,
@@ -305,14 +308,14 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
QVERIFY(formats.isEmpty());
// QTCREATORBUG-29218
highlighter->clearAllExtraFormats();
highlighterRunner->clearAllExtraFormats();
const HighlightingResults bug29218Results{HighlightingResult(1, 1, 2, 0),
HighlightingResult(1, 3, 2, 1)};
QFutureInterface<HighlightingResult> fi29218;
fi29218.reportResults(bug29218Results);
formats = firstBlock.layout()->formats();
QVERIFY(formats.isEmpty());
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighterRunner,
fi29218.future(),
0,
1,
@@ -323,7 +326,7 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
QCOMPARE(formats.at(0).format.fontOverline(), false);
QCOMPARE(formats.at(0).start, 0);
QCOMPARE(formats.at(0).length, 2);
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighterRunner,
fi29218.future(),
1,
2,
@@ -347,7 +350,7 @@ void tst_highlighter::test_preeditText()
QTextBlock firstBlock = doc->findBlockByNumber(0);
firstBlock.layout()->setPreeditArea(2, "uaf");
SemanticHighlighter::setExtraAdditionalFormats(highlighter, highlightingResults(), formatHash);
SemanticHighlighter::setExtraAdditionalFormats(highlighterRunner, highlightingResults(), formatHash);
auto formats = firstBlock.layout()->formats();
QCOMPARE(formats.size(), 2);
@@ -366,7 +369,7 @@ void tst_highlighter::cleanup()
{
delete doc;
doc = nullptr;
highlighter = nullptr;
highlighterRunner = nullptr;
}
} // namespace TextEditor