Files
qt-creator/src/plugins/texteditor/syntaxhighlighterrunner.cpp

276 lines
8.9 KiB
C++
Raw Normal View History

2023-05-08 17:11:54 +02:00
// 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 "fontsettings.h"
#include "textdocumentlayout.h"
#include "texteditorsettings.h"
#include "highlighter.h"
2023-05-08 17:11:54 +02:00
#include <utils/algorithm.h>
2023-05-08 17:11:54 +02:00
#include <utils/textutils.h>
#include <QMetaObject>
#include <QTextCursor>
#include <QTextDocument>
#include <QThread>
namespace TextEditor {
struct BlockPreeditData {
int position;
QString text;
};
2023-05-08 17:11:54 +02:00
class SyntaxHighlighterRunnerPrivate : public QObject
{
Q_OBJECT
public:
SyntaxHighlighterRunnerPrivate(SyntaxHighlighter *highlighter,
QTextDocument *document,
bool async)
: m_highlighter(highlighter)
{
if (async) {
m_document = new QTextDocument(this);
m_document->setDocumentLayout(new TextDocumentLayout(m_document));
m_highlighter->setParent(m_document);
} else {
m_document = document;
}
m_highlighter->setDocument(m_document);
connect(m_highlighter,
&SyntaxHighlighter::resultsReady,
this,
&SyntaxHighlighterRunnerPrivate::resultsReady);
}
void changeDocument(int from,
int charsRemoved,
const QString textAdded,
const QMap<int, BlockPreeditData> &blocksPreedit)
2023-05-08 17:11:54 +02:00
{
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->findBlockByNumber(it.key());
2023-05-08 17:11:54 +02:00
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);
rehighlight();
2023-05-08 17:11:54 +02:00
}
void setDefinitionName(const QString &name)
{
return m_highlighter->setDefinitionName(name);
}
2023-05-08 17:11:54 +02:00
void setLanguageFeaturesFlags(unsigned int flags)
{
m_highlighter->setLanguageFeaturesFlags(flags);
}
void setEnabled(bool enabled) { m_highlighter->setEnabled(enabled); }
void rehighlight() { m_highlighter->rehighlight(); }
SyntaxHighlighter *m_highlighter = nullptr;
2023-05-08 17:11:54 +02:00
QTextDocument *m_document = nullptr;
signals:
void resultsReady(const QList<SyntaxHighlighter::Result> &result);
2023-05-08 17:11:54 +02:00
};
SyntaxHighlighterRunner::SyntaxHighlighterRunner(SyntaxHighlighter *highlighter,
QTextDocument *document,
bool async)
: d(new SyntaxHighlighterRunnerPrivate(highlighter, document, async))
, m_document(document)
2023-05-08 17:11:54 +02:00
{
m_useGenericHighlighter = qobject_cast<Highlighter *>(d->m_highlighter);
if (async) {
m_thread.emplace();
d->moveToThread(&*m_thread);
connect(&*m_thread, &QThread::finished, d, &QObject::deleteLater);
m_thread->start();
connect(d,
&SyntaxHighlighterRunnerPrivate::resultsReady,
this,
&SyntaxHighlighterRunner::applyFormatRanges);
changeDocument(0, 0, document->characterCount());
connect(document,
&QTextDocument::contentsChange,
this,
&SyntaxHighlighterRunner::changeDocument);
m_foldValidator.setup(qobject_cast<TextDocumentLayout *>(document->documentLayout()));
} else {
connect(d,
&SyntaxHighlighterRunnerPrivate::resultsReady,
this,
[this](const QList<SyntaxHighlighter::Result> &result) {
auto done = std::find_if(result.cbegin(),
result.cend(),
[](const SyntaxHighlighter::Result &res) {
return res.m_state == SyntaxHighlighter::State::Done;
});
if (done != result.cend()) {
m_syntaxInfoUpdated = SyntaxHighlighter::State::Done;
emit highlightingFinished();
return;
}
m_syntaxInfoUpdated = SyntaxHighlighter::State::InProgress;
});
}
2023-05-08 17:11:54 +02:00
}
SyntaxHighlighterRunner::~SyntaxHighlighterRunner()
{
if (m_thread) {
m_thread->requestInterruption();
m_thread->quit();
m_thread->wait();
} else {
delete d->m_highlighter;
delete d;
}
}
2023-05-08 17:11:54 +02:00
void SyntaxHighlighterRunner::applyFormatRanges(const QList<SyntaxHighlighter::Result> &results)
2023-05-08 17:11:54 +02:00
{
if (m_document == nullptr)
return;
for (const SyntaxHighlighter::Result &result : results) {
m_syntaxInfoUpdated = result.m_state;
if (m_syntaxInfoUpdated == SyntaxHighlighter::State::Start) {
m_foldValidator.reset();
continue;
}
if (m_syntaxInfoUpdated == SyntaxHighlighter::State::Done) {
m_foldValidator.finalize();
emit highlightingFinished();
return;
}
QTextBlock docBlock = m_document->findBlock(result.m_blockNumber);
if (!docBlock.isValid())
return;
2023-05-08 17:11:54 +02:00
result.copyToBlock(docBlock);
2023-05-08 17:11:54 +02:00
if (result.m_formatRanges != docBlock.layout()->formats()) {
docBlock.layout()->setFormats(result.m_formatRanges);
m_document->markContentsDirty(docBlock.position(), docBlock.length());
}
if (m_syntaxInfoUpdated != SyntaxHighlighter::State::Extras)
m_foldValidator.process(docBlock);
2023-05-08 17:11:54 +02:00
}
}
void SyntaxHighlighterRunner::changeDocument(int from, int charsRemoved, int charsAdded)
2023-05-08 17:11:54 +02:00
{
QTC_ASSERT(m_document, return);
m_syntaxInfoUpdated = SyntaxHighlighter::State::InProgress;
QMap<int, BlockPreeditData> blocksPreedit;
QTextBlock block = m_document->findBlock(from);
const QTextBlock endBlock = m_document->findBlock(from + charsAdded);
while (block.isValid() && block != endBlock) {
if (QTextLayout *layout = block.layout()) {
if (const int pos = layout->preeditAreaPosition(); pos != -1)
blocksPreedit[block.blockNumber()] = {pos, layout->preeditAreaText()};
2023-05-08 17:11:54 +02:00
}
block = block.next();
2023-05-08 17:11:54 +02:00
}
const QString text = Utils::Text::textAt(QTextCursor(m_document), from, charsAdded);
QMetaObject::invokeMethod(d, [this, from, charsRemoved, text, blocksPreedit] {
d->changeDocument(from, charsRemoved, text, blocksPreedit);
2023-05-08 17:11:54 +02:00
});
}
bool SyntaxHighlighterRunner::useGenericHighlighter() const
{
return m_useGenericHighlighter;
}
void SyntaxHighlighterRunner::setExtraFormats(
2023-05-08 17:11:54 +02:00
const QMap<int, QList<QTextLayout::FormatRange>> &formatMap)
{
QMetaObject::invokeMethod(d, [this, formatMap] { d->setExtraFormats(formatMap); });
2023-05-08 17:11:54 +02:00
}
void SyntaxHighlighterRunner::clearExtraFormats(const QList<int> &blockNumbers)
2023-05-08 17:11:54 +02:00
{
QMetaObject::invokeMethod(d, [this, blockNumbers] { d->clearExtraFormats(blockNumbers); });
2023-05-08 17:11:54 +02:00
}
void SyntaxHighlighterRunner::clearAllExtraFormats()
2023-05-08 17:11:54 +02:00
{
QMetaObject::invokeMethod(d, [this] { d->clearAllExtraFormats(); });
2023-05-08 17:11:54 +02:00
}
void SyntaxHighlighterRunner::setFontSettings(const TextEditor::FontSettings &fontSettings)
2023-05-08 17:11:54 +02:00
{
QMetaObject::invokeMethod(d, [this, fontSettings] { d->setFontSettings(fontSettings); });
2023-05-08 17:11:54 +02:00
}
void SyntaxHighlighterRunner::setLanguageFeaturesFlags(unsigned int flags)
2023-05-08 17:11:54 +02:00
{
QMetaObject::invokeMethod(d, [this, flags] { d->setLanguageFeaturesFlags(flags); });
2023-05-08 17:11:54 +02:00
}
void SyntaxHighlighterRunner::setEnabled(bool enabled)
2023-05-08 17:11:54 +02:00
{
QMetaObject::invokeMethod(d, [this, enabled] { d->setEnabled(enabled); });
2023-05-08 17:11:54 +02:00
}
void SyntaxHighlighterRunner::rehighlight()
2023-05-08 17:11:54 +02:00
{
QMetaObject::invokeMethod(d, [this] { d->rehighlight(); });
2023-05-08 17:11:54 +02:00
}
QString SyntaxHighlighterRunner::definitionName()
{
return m_definitionName;
}
void SyntaxHighlighterRunner::setDefinitionName(const QString &name)
2023-05-08 17:11:54 +02:00
{
if (name.isEmpty())
return;
m_definitionName = name;
QMetaObject::invokeMethod(d, [this, name] { d->setDefinitionName(name); });
2023-05-08 17:11:54 +02:00
}
} // namespace TextEditor
#include "syntaxhighlighterrunner.moc"