Skip to content

Commit

Permalink
don't reset KSyntaxhigHlighter state every line
Browse files Browse the repository at this point in the history
If the state is reset every line KSyntaxHighlighter will be unable to
highlight multi line comments (for example). This patch changes that by
highlighting everyting at once.
  • Loading branch information
lievenhey committed Jan 16, 2024
1 parent 9bcbbc5 commit a3b2909
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 42 deletions.
122 changes: 85 additions & 37 deletions src/models/highlightedtext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

#include "formattingutils.h"

using LineFormat = QVector<QTextLayout::FormatRange>;

#if KFSyntaxHighlighting_FOUND
// highlighter using KSyntaxHighlighting
class HighlightingImplementation : public KSyntaxHighlighting::AbstractHighlighter
Expand All @@ -38,13 +40,25 @@ class HighlightingImplementation : public KSyntaxHighlighting::AbstractHighlight
}
~HighlightingImplementation() override = default;

virtual QVector<QTextLayout::FormatRange> format(const QString& text)
virtual void formatText(const QStringList& text)
{
if (m_lines != text) {
m_lines = text;
}

m_formats.clear();
KSyntaxHighlighting::State state = {};

highlightLine(text, {});
for (const auto& line : text) {
m_lineFormat.clear();
state = highlightLine(line, state);
m_formats.push_back(m_lineFormat);
}
}

return m_formats;
virtual LineFormat format(int lineIndex) const
{
return m_formats.at(lineIndex);
}

virtual void themeChanged()
Expand All @@ -66,6 +80,7 @@ class HighlightingImplementation : public KSyntaxHighlighting::AbstractHighlight
virtual void setHighlightingDefinition(const KSyntaxHighlighting::Definition& definition)
{
setDefinition(definition);
formatText(m_lines);
}

virtual QString definitionName() const
Expand All @@ -79,33 +94,60 @@ class HighlightingImplementation : public KSyntaxHighlighting::AbstractHighlight
QTextCharFormat textCharFormat;
textCharFormat.setForeground(format.textColor(theme()));
textCharFormat.setFontWeight(format.isBold(theme()) ? QFont::Bold : QFont::Normal);
m_formats.push_back({offset, length, textCharFormat});
m_lineFormat.push_back({offset, length, textCharFormat});
}

private:
virtual LineFormat formatLine(const QString& /*line*/)
{
return {};
}

KSyntaxHighlighting::Repository* m_repository;
QVector<QTextLayout::FormatRange> m_formats;
QStringList m_lines; // for reformatting if definition changes
LineFormat m_lineFormat;
QVector<LineFormat> m_formats;
};
#else
// stub incase KSyntaxHighlighting is not available
// stub in case KSyntaxHighlighting is not available
class HighlightingImplementation
{
public:
virtual HighlightingImplementation(KSyntaxHighlighting::Repository*) = default;
~HighlightingImplementation() override = default;
explicit HighlightingImplementation(KSyntaxHighlighting::Repository* /* repository */) { }
virtual ~HighlightingImplementation() = default;

virtual QVector<QTextLayout::FormatRange> format(const QStringList& text) override
void formatText(const QStringList& text)
{
return {};
m_formats.clear();

for (const auto& line : text) {
m_formats.push_back(formatLine(line));
}
}

LineFormat format(int lineIndex) const
{
return m_formats.at(lineIndex);
}

virtual void themeChanged() override { }
virtual void themeChanged() { }

virtual void setHighlightingDefinition(const KSyntaxHighlighting::Definition& /*definition*/) override { }
virtual QString definitionName() const override
virtual void setHighlightingDefinition(const KSyntaxHighlighting::Definition& /*definition*/) { }
virtual QString definitionName() const
{
return {};
};

private:
// stub implementation necessary for testing
virtual LineFormat formatLine(const QString& line)
{
return {{QTextLayout::FormatRange {0, line.length(), {}}}};
}

Q_DISABLE_COPY(HighlightingImplementation)
QVector<LineFormat> m_formats;
};
#endif

class AnsiHighlightingImplementation : public HighlightingImplementation
Expand All @@ -117,7 +159,19 @@ class AnsiHighlightingImplementation : public HighlightingImplementation
}
~AnsiHighlightingImplementation() override = default;

QVector<QTextLayout::FormatRange> format(const QString& text) final
void themeChanged() override
{
m_colorScheme = KColorScheme(QPalette::Normal, KColorScheme::Complementary);
}

void setHighlightingDefinition(const KSyntaxHighlighting::Definition& /*definition*/) override { }
QString definitionName() const override
{
return {};
}

private:
LineFormat formatLine(const QString& text) override
{
QVector<QTextLayout::FormatRange> formats;

Expand Down Expand Up @@ -166,28 +220,17 @@ class AnsiHighlightingImplementation : public HighlightingImplementation
return formats;
}

void themeChanged() override
{
m_colorScheme = KColorScheme(QPalette::Normal, KColorScheme::Complementary);
}

void setHighlightingDefinition(const KSyntaxHighlighting::Definition& /*definition*/) override { }
QString definitionName() const override
{
return {};
}

private:
KColorScheme m_colorScheme;
};

// QTextLayout is slow, this class acts as a cache that only creates and fills the QTextLayout on demand
class HighlightedLine
{
public:
HighlightedLine(HighlightingImplementation* highlighter, QString text)
HighlightedLine(HighlightingImplementation* highlighter, const QString& text, int index)
: m_highlighter(highlighter)
, m_text(std::move(text))
, m_text(Util::removeAnsi(text))
, m_index(index)
, m_layout(nullptr)
{
}
Expand Down Expand Up @@ -215,12 +258,10 @@ class HighlightedLine
if (!m_layout) {
m_layout = std::make_unique<QTextLayout>();
m_layout->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
const auto& ansiFreeLine = Util::removeAnsi(m_text);
m_layout->setText(ansiFreeLine);
m_layout->setText(m_text);
}

m_layout->setFormats(m_highlighter->format(m_text));

m_layout->setFormats(m_highlighter->format(m_index));
m_layout->beginLayout();

// there is at most one line, so we don't need to check this multiple times
Expand All @@ -233,15 +274,15 @@ class HighlightedLine

HighlightingImplementation* m_highlighter;
QString m_text;
int m_index;
std::unique_ptr<QTextLayout> m_layout;
};

HighlightedText::HighlightedText(KSyntaxHighlighting::Repository* repository, QObject* parent)
: QObject(parent)
#if KFSyntaxHighlighting_FOUND
, m_repository(repository)
#endif
{
Q_UNUSED(repository);
}

HighlightedText::~HighlightedText() = default;
Expand All @@ -263,9 +304,12 @@ void HighlightedText::setText(const QStringList& text)
}

m_highlightedLines.reserve(text.size());
std::transform(text.cbegin(), text.cend(), std::back_inserter(m_highlightedLines), [this](const QString& text) {
return HighlightedLine {m_highlighter.get(), text};
});
m_highlighter->formatText(text);
int index = 0;
std::transform(text.cbegin(), text.cend(), std::back_inserter(m_highlightedLines),
[this, &index](const QString& text) {
return HighlightedLine {m_highlighter.get(), text, index++};
});

connect(this, &HighlightedText::definitionChanged, this, &HighlightedText::updateHighlighting);

Expand All @@ -275,9 +319,13 @@ void HighlightedText::setText(const QStringList& text)

void HighlightedText::setDefinition(const KSyntaxHighlighting::Definition& definition)
{
#if KFSyntaxHighlighting_FOUND
Q_ASSERT(m_highlighter);
m_highlighter->setHighlightingDefinition(definition);
emit definitionChanged(definition.name());
#else
Q_UNUSED(definition);
#endif
}

QString HighlightedText::textAt(int index) const
Expand Down
2 changes: 0 additions & 2 deletions src/models/highlightedtext.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ public slots:
void updateHighlighting();

private:
#if KFSyntaxHighlighting_FOUND
KSyntaxHighlighting::Repository* m_repository;
#endif
std::unique_ptr<HighlightingImplementation> m_highlighter;
mutable std::vector<HighlightedLine> m_highlightedLines;
QStringList m_lines;
Expand Down
5 changes: 2 additions & 3 deletions tests/modeltests/tst_formatting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,8 @@ private slots:
QTest::addColumn<QStringList>("ansiStrings");
QTest::addColumn<QVector<QVector<QTextLayout::FormatRange>>>("formatting");

QTest::addRow("no ansi sequence")
<< QStringList {QStringLiteral(" A B C D E ")}
<< QVector<QVector<QTextLayout::FormatRange>> {{{0, 15, {}}}}; // only default formatting
QTest::addRow("no ansi sequence") << QStringList {QStringLiteral(" A B C D E ")}
<< QVector<QVector<QTextLayout::FormatRange>> {{{0, 15, {}}}};
QTest::addRow("one ansi sequence") << QStringList {QStringLiteral("\u001B[33mHello World\u001B[0m")}
<< QVector<QVector<QTextLayout::FormatRange>> {{{0, 11, {}}}};
QTest::addRow("two ansi sequences")
Expand Down

0 comments on commit a3b2909

Please sign in to comment.