diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp index 23916bf00f3..0be1c14fc4d 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.cpp +++ b/src/plugins/clangformat/clangformatconfigwidget.cpp @@ -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); diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index 3d62aadcd65..8d4adebf141 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -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(syntaxHighlighter()); - highlighter->setLanguageFeatures(document->languageFeatures()); + if (BaseSyntaxHighlighterRunner *highlighter = syntaxHighlighterRunner()) + highlighter->setLanguageFeaturesFlags(document->languageFeatures().flags); m_overviewModel.update(usesClangd() ? nullptr : document); diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp index 142b2b296c9..591881c6c5b 100644 --- a/src/plugins/cppeditor/cpphighlighter.cpp +++ b/src/plugins/cppeditor/cpphighlighter.cpp @@ -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(); } } diff --git a/src/plugins/cppeditor/cpphighlighter.h b/src/plugins/cppeditor/cpphighlighter.h index e2fac20bba6..5a6af63adaf 100644 --- a/src/plugins/cppeditor/cpphighlighter.h +++ b/src/plugins/cppeditor/cpphighlighter.h @@ -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: diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp index c8ab16c3228..3c9f6eeec33 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.cpp +++ b/src/plugins/cppeditor/cpptoolsreuse.cpp @@ -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); diff --git a/src/plugins/cppeditor/semantichighlighter.cpp b/src/plugins/cppeditor/semantichighlighter.cpp index 156a1d40524..499b1eef6c9 100644 --- a/src/plugins/cppeditor/semantichighlighter.cpp +++ b/src/plugins/cppeditor/semantichighlighter.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -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()); diff --git a/src/plugins/diffeditor/diffeditor.cpp b/src/plugins/diffeditor/diffeditor.cpp index 4998dfbd9a0..59baa0c273a 100644 --- a/src/plugins/diffeditor/diffeditor.cpp +++ b/src/plugins/diffeditor/diffeditor.cpp @@ -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 diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp index 2136846be0d..b6223f80207 100644 --- a/src/plugins/git/giteditor.cpp +++ b/src/plugins/git/giteditor.cpp @@ -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); }); } diff --git a/src/plugins/languageclient/semantichighlightsupport.cpp b/src/plugins/languageclient/semantichighlightsupport.cpp index 472f55b4976..655026c9c26 100644 --- a/src/plugins/languageclient/semantichighlightsupport.cpp +++ b/src/plugins/languageclient/semantichighlightsupport.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -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); diff --git a/src/plugins/nim/editor/nimeditorfactory.cpp b/src/plugins/nim/editor/nimeditorfactory.cpp index 62e49595385..92d6f05c0f3 100644 --- a/src/plugins/nim/editor/nimeditorfactory.cpp +++ b/src/plugins/nim/editor/nimeditorfactory.cpp @@ -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())); } diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp index 6f6e3ffaa84..58917c0954b 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp @@ -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())); diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index a8cdb569269..8219243c29d 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -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); } diff --git a/src/plugins/qmljseditor/qmljseditordocument.cpp b/src/plugins/qmljseditor/qmljseditordocument.cpp index 5923521ab06..2c145f38c7c 100644 --- a/src/plugins/qmljseditor/qmljseditordocument.cpp +++ b/src/plugins/qmljseditor/qmljseditordocument.cpp @@ -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())); } diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp index 67e14dfbcd5..c414684df7a 100644 --- a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp +++ b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp @@ -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 &promise, diff --git a/src/plugins/texteditor/CMakeLists.txt b/src/plugins/texteditor/CMakeLists.txt index 3f38c4436fc..939c313712b 100644 --- a/src/plugins/texteditor/CMakeLists.txt +++ b/src/plugins/texteditor/CMakeLists.txt @@ -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 diff --git a/src/plugins/texteditor/highlighter.cpp b/src/plugins/texteditor/highlighter.cpp index 14e2f1ff168..49276a61c76 100644 --- a/src/plugins/texteditor/highlighter.cpp +++ b/src/plugins/texteditor/highlighter.cpp @@ -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('('); diff --git a/src/plugins/texteditor/highlighter.h b/src/plugins/texteditor/highlighter.h index 0b99265aa52..3cb3edce620 100644 --- a/src/plugins/texteditor/highlighter.h +++ b/src/plugins/texteditor/highlighter.h @@ -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; diff --git a/src/plugins/texteditor/highlighter_test.cpp b/src/plugins/texteditor/highlighter_test.cpp index f38e6a4f854..ef0718f65b4 100644 --- a/src/plugins/texteditor/highlighter_test.cpp +++ b/src/plugins/texteditor/highlighter_test.cpp @@ -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(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; @@ -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)}, diff --git a/src/plugins/texteditor/highlighterhelper.cpp b/src/plugins/texteditor/highlighterhelper.cpp index abe8b3ceeca..f27c2f3f394 100644 --- a/src/plugins/texteditor/highlighterhelper.cpp +++ b/src/plugins/texteditor/highlighterhelper.cpp @@ -9,6 +9,7 @@ #include "texteditor.h" #include "texteditorsettings.h" #include "texteditortr.h" +#include "syntaxhighlighterrunner.h" #include #include @@ -209,7 +210,8 @@ void reload() highlightRepository()->reload(); for (auto editor : Core::DocumentModel::editorsForOpenedDocuments()) { if (auto textEditor = qobject_cast(editor)) { - if (qobject_cast(textEditor->textDocument()->syntaxHighlighter())) + auto highlighterCreator = textEditor->textDocument()->syntaxHighlighterRunner()->creator(); + if (highlighterCreator && qobject_cast(highlighterCreator())) textEditor->editorWidget()->configureGenericHighlighter(); } } diff --git a/src/plugins/texteditor/semantichighlighter.cpp b/src/plugins/texteditor/semantichighlighter.cpp index 30cb9cf729a..b4f4245036d 100644 --- a/src/plugins/texteditor/semantichighlighter.cpp +++ b/src/plugins/texteditor/semantichighlighter.cpp @@ -5,6 +5,7 @@ #include "syntaxhighlighter.h" #include "texteditorsettings.h" +#include "syntaxhighlighterrunner.h" #include @@ -73,11 +74,13 @@ const Ranges rangesForResult( } -void SemanticHighlighter::incrementalApplyExtraAdditionalFormats(SyntaxHighlighter *highlighter, - const QFuture &future, - int from, int to, - const QHash &kindToFormat, - const Splitter &splitter) +void SemanticHighlighter::incrementalApplyExtraAdditionalFormats( + BaseSyntaxHighlighterRunner *highlighter, + const QFuture &future, + int from, + int to, + const QHash &kindToFormat, + const Splitter &splitter) { if (to <= from) return; @@ -111,17 +114,21 @@ void SemanticHighlighter::incrementalApplyExtraAdditionalFormats(SyntaxHighlight formatRanges[range.block].append(range.formatRange); } + QList clearBlockNumberVector; + QMap> 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 &results, const QHash &kindToFormat) { @@ -132,20 +139,19 @@ void SemanticHighlighter::setExtraAdditionalFormats(SyntaxHighlighter *highlight QTextDocument *doc = highlighter->document(); QTC_ASSERT(doc, return ); - std::map> formatRanges; + QMap> 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 &future) + BaseSyntaxHighlighterRunner *highlighter, const QFuture &future) { const QTextDocument * const doc = highlighter->document(); QTextBlock firstBlockToClear = doc->begin(); @@ -160,6 +166,9 @@ void SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd( } } + QList clearBlockNumberVector; for (QTextBlock b = firstBlockToClear; b.isValid(); b = b.next()) - highlighter->clearExtraFormats(b); + clearBlockNumberVector.append(b.blockNumber()); + + highlighter->clearExtraFormats(clearBlockNumberVector); } diff --git a/src/plugins/texteditor/semantichighlighter.h b/src/plugins/texteditor/semantichighlighter.h index 7ef35bc4c23..fa841f5b2c6 100644 --- a/src/plugins/texteditor/semantichighlighter.h +++ b/src/plugins/texteditor/semantichighlighter.h @@ -21,6 +21,7 @@ QT_END_NAMESPACE namespace TextEditor { class SyntaxHighlighter; +class BaseSyntaxHighlighterRunner; class TEXTEDITOR_EXPORT HighlightingResult { @@ -72,29 +73,27 @@ using Splitter = std::function &future, - int from, int to, - const QHash &kindToFormat, - const Splitter &splitter = {}); +void TEXTEDITOR_EXPORT +incrementalApplyExtraAdditionalFormats(BaseSyntaxHighlighterRunner *highlighter, + const QFuture &future, + int from, + int to, + const QHash &kindToFormat, + const Splitter &splitter = {}); // Clears all extra highlights and applies the extra formats // 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, - const HighlightingResults &results, - const QHash &kindToFormat); +void TEXTEDITOR_EXPORT setExtraAdditionalFormats(BaseSyntaxHighlighterRunner *highlighter, + const HighlightingResults &results, + const QHash &kindToFormat); // Cleans the extra additional formats after the last result of the Future // until the end of the document. // Requires that results of the Future are ordered by line. void TEXTEDITOR_EXPORT clearExtraAdditionalFormatsUntilEnd( - SyntaxHighlighter *highlighter, - const QFuture &future); - + BaseSyntaxHighlighterRunner *highlighter, const QFuture &future); } // namespace SemanticHighlighter } // namespace TextEditor diff --git a/src/plugins/texteditor/syntaxhighlighter.cpp b/src/plugins/texteditor/syntaxhighlighter.cpp index 36c4549487c..0d7b4d2ede9 100644 --- a/src/plugins/texteditor/syntaxhighlighter.cpp +++ b/src/plugins/texteditor/syntaxhighlighter.cpp @@ -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 formatChanges; + QList formatChanges; QTextBlock currentBlock; bool rehighlightPending = false; bool inReformatBlocks = false; TextDocumentLayout::FoldValidator foldValidator; - QVector formats; - QVector> formatCategories; + QList formats; + QList> formatCategories; QTextCharFormat whitespaceFormat; bool noAutomaticHighlighting = false; }; @@ -102,8 +101,8 @@ void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, in QTextLayout *layout = currentBlock.layout(); - QVector ranges; - QVector oldRanges; + QList ranges; + QList 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 newRanges; + QList 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 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 &&formats) + const QList &formats) { Q_D(SyntaxHighlighter); + QList formatsCopy = formats; + const int blockLength = block.length(); if (block.layout() == nullptr || blockLength == 0) return; const QString preeditText = block.layout()->preeditAreaText(); if (!preeditText.isEmpty()) { - QVector additionalRanges; + QList 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 all = block.layout()->formats(); - QVector previousSemanticFormats; - QVector formatsToApply; + const QList all = block.layout()->formats(); + QList previousSemanticFormats; + QList 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 formatsToApply + const QList 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 formatMapping) { - QVector> categories; + QList> 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> &categories) +void SyntaxHighlighter::setTextFormatCategories(const QList> &categories) { Q_D(SyntaxHighlighter); d->formatCategories = categories; const int maxCategory = Utils::maxElementOr(categories, {-1, C_TEXT}).first; - d->formats = QVector(maxCategory + 1); + d->formats = QList(maxCategory + 1); d->updateFormats(TextEditorSettings::fontSettings()); } @@ -899,4 +926,3 @@ void SyntaxHighlighterPrivate::updateFormats(const FontSettings &fontSettings) } // namespace TextEditor -#include "moc_syntaxhighlighter.cpp" diff --git a/src/plugins/texteditor/syntaxhighlighter.h b/src/plugins/texteditor/syntaxhighlighter.h index 78e0e031849..1e9ffdabffc 100644 --- a/src/plugins/texteditor/syntaxhighlighter.h +++ b/src/plugins/texteditor/syntaxhighlighter.h @@ -6,6 +6,9 @@ #include "texteditor_global.h" #include +#include + +#include #include #include @@ -41,10 +44,6 @@ public: void setDocument(QTextDocument *doc); QTextDocument *document() const; - void setExtraFormats(const QTextBlock &block, QVector &&formats); - void clearExtraFormats(const QTextBlock &block); - void clearAllExtraFormats(); - static QList 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 m_formatRanges; + }; + + void setExtraFormats(const QTextBlock &block, const QList &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); + private: - void setTextFormatCategories(const QVector> &categories); + void setTextFormatCategories(const QList> &categories); void reformatBlocks(int from, int charsRemoved, int charsAdded); void delayedRehighlight(); diff --git a/src/plugins/texteditor/syntaxhighlighterrunner.cpp b/src/plugins/texteditor/syntaxhighlighterrunner.cpp new file mode 100644 index 00000000000..9ba547da946 --- /dev/null +++ b/src/plugins/texteditor/syntaxhighlighterrunner.cpp @@ -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 +#include + +#include + +#include +#include +#include +#include + +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 &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> &formatMap) + { + for (auto it = formatMap.cbegin(); it != formatMap.cend(); ++it) + m_highlighter->setExtraFormats(m_document->findBlockByNumber(it.key()), it.value()); + } + + void clearExtraFormats(const QList &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 &result); + +private: + BaseSyntaxHighlighterRunner::SyntaxHighLighterCreator m_creator; + std::unique_ptr 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(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 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 &blocksPreedit) +{ + QMetaObject::invokeMethod(d.get(), [this, from, charsRemoved, textAdded, blocksPreedit] { + d->cloneDocument(from, charsRemoved, textAdded, blocksPreedit); + }); +} + +void BaseSyntaxHighlighterRunner::setExtraFormats( + const QMap> &formatMap) +{ + QMetaObject::invokeMethod(d.get(), [this, formatMap] { d->setExtraFormats(formatMap); }); +} + +void BaseSyntaxHighlighterRunner::clearExtraFormats(const QList &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 &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" diff --git a/src/plugins/texteditor/syntaxhighlighterrunner.h b/src/plugins/texteditor/syntaxhighlighterrunner.h new file mode 100644 index 00000000000..984b227e77b --- /dev/null +++ b/src/plugins/texteditor/syntaxhighlighterrunner.h @@ -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 + +#include + +#include +#include + +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; + struct BlockPreeditData { + int position; + QString text; + }; + + BaseSyntaxHighlighterRunner(SyntaxHighLighterCreator creator, QTextDocument *document); + virtual ~BaseSyntaxHighlighterRunner(); + + void setExtraFormats(const QMap> &formats); + void clearExtraFormats(const QList &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 d; + QPointer 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 &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 + diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index 2a6f4da0a7c..0f19b922048 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -13,11 +13,13 @@ #include "texteditortr.h" #include "textindenter.h" #include "typingsettings.h" +#include "syntaxhighlighterrunner.h" #include #include #include #include +#include #include #include @@ -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 &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) diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h index e05abbdba39..c7fe881d27e 100644 --- a/src/plugins/texteditor/textdocument.h +++ b/src/plugins/texteditor/textdocument.h @@ -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; - 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); diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 80d72a4ded8..0d0f66e6b13 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -34,6 +34,7 @@ #include "texteditorsettings.h" #include "texteditortr.h" #include "typingsettings.h" +#include "syntaxhighlighterrunner.h" #include @@ -1930,9 +1931,11 @@ void TextEditorWidgetPrivate::foldLicenseHeader() const QString trimmedText = text.trimmed(); QStringList commentMarker; QStringList docMarker; - if (auto highlighter = qobject_cast( - 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(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 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); diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs index 77f80a51a58..4f64752c122 100644 --- a/src/plugins/texteditor/texteditor.qbs +++ b/src/plugins/texteditor/texteditor.qbs @@ -124,6 +124,8 @@ QtcPlugin { "storagesettings.h", "syntaxhighlighter.cpp", "syntaxhighlighter.h", + "syntaxhighlighterrunner.cpp", + "syntaxhighlighterrunner.h", "tabsettings.cpp", "tabsettings.h", "tabsettingswidget.cpp", diff --git a/src/plugins/vcsbase/diffandloghighlighter.cpp b/src/plugins/vcsbase/diffandloghighlighter.cpp index 3cb27b9edb9..6b4eb2ba0b3 100644 --- a/src/plugins/vcsbase/diffandloghighlighter.cpp +++ b/src/plugins/vcsbase/diffandloghighlighter.cpp @@ -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 diff --git a/src/plugins/vcsbase/diffandloghighlighter.h b/src/plugins/vcsbase/diffandloghighlighter.h index 6318de941f3..7caea12d52f 100644 --- a/src/plugins/vcsbase/diffandloghighlighter.h +++ b/src/plugins/vcsbase/diffandloghighlighter.h @@ -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; diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp index 93fb8af7e09..539d10f6aaa 100644 --- a/src/plugins/vcsbase/vcsbaseeditor.cpp +++ b/src/plugins/vcsbase/vcsbaseeditor.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -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(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(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); }); } } diff --git a/tests/auto/texteditor/highlighter/tst_highlighter.cpp b/tests/auto/texteditor/highlighter/tst_highlighter.cpp index 7c30d3371ea..f9060afae5b 100644 --- a/tests/auto/texteditor/highlighter/tst_highlighter.cpp +++ b/tests/auto/texteditor/highlighter/tst_highlighter.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -31,7 +32,7 @@ private slots: private: QTextDocument *doc = nullptr; - SyntaxHighlighter *highlighter = nullptr; + BaseSyntaxHighlighterRunner *highlighterRunner = nullptr; FontSettings fontsettings; QHash 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 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 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 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