diff --git a/src/models/highlightedtext.cpp b/src/models/highlightedtext.cpp index 60966626..dff85cb2 100644 --- a/src/models/highlightedtext.cpp +++ b/src/models/highlightedtext.cpp @@ -27,6 +27,8 @@ #include "formattingutils.h" +using LineFormat = QVector; + #if KFSyntaxHighlighting_FOUND // highlighter using KSyntaxHighlighting class HighlightingImplementation : public KSyntaxHighlighting::AbstractHighlighter @@ -38,13 +40,25 @@ class HighlightingImplementation : public KSyntaxHighlighting::AbstractHighlight } ~HighlightingImplementation() override = default; - virtual QVector 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() @@ -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 @@ -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 m_formats; + QStringList m_lines; // for reformatting if definition changes + LineFormat m_lineFormat; + QVector 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 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 m_formats; +}; #endif class AnsiHighlightingImplementation : public HighlightingImplementation @@ -117,7 +159,19 @@ class AnsiHighlightingImplementation : public HighlightingImplementation } ~AnsiHighlightingImplementation() override = default; - QVector 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 formats; @@ -166,18 +220,6 @@ 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; }; @@ -185,9 +227,10 @@ class AnsiHighlightingImplementation : public HighlightingImplementation 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) { } @@ -215,12 +258,10 @@ class HighlightedLine if (!m_layout) { m_layout = std::make_unique(); 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 @@ -233,15 +274,15 @@ class HighlightedLine HighlightingImplementation* m_highlighter; QString m_text; + int m_index; std::unique_ptr m_layout; }; HighlightedText::HighlightedText(KSyntaxHighlighting::Repository* repository, QObject* parent) : QObject(parent) -#if KFSyntaxHighlighting_FOUND , m_repository(repository) -#endif { + Q_UNUSED(repository); } HighlightedText::~HighlightedText() = default; @@ -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); @@ -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 diff --git a/src/models/highlightedtext.h b/src/models/highlightedtext.h index 88d2db06..d41a0b12 100644 --- a/src/models/highlightedtext.h +++ b/src/models/highlightedtext.h @@ -55,9 +55,7 @@ public slots: void updateHighlighting(); private: -#if KFSyntaxHighlighting_FOUND KSyntaxHighlighting::Repository* m_repository; -#endif std::unique_ptr m_highlighter; mutable std::vector m_highlightedLines; QStringList m_lines; diff --git a/tests/modeltests/tst_formatting.cpp b/tests/modeltests/tst_formatting.cpp index cb2dba31..1ceeb546 100644 --- a/tests/modeltests/tst_formatting.cpp +++ b/tests/modeltests/tst_formatting.cpp @@ -46,9 +46,8 @@ private slots: QTest::addColumn("ansiStrings"); QTest::addColumn>>("formatting"); - QTest::addRow("no ansi sequence") - << QStringList {QStringLiteral(" A B C D E ")} - << QVector> {{{0, 15, {}}}}; // only default formatting + QTest::addRow("no ansi sequence") << QStringList {QStringLiteral(" A B C D E ")} + << QVector> {{{0, 15, {}}}}; QTest::addRow("one ansi sequence") << QStringList {QStringLiteral("\u001B[33mHello World\u001B[0m")} << QVector> {{{0, 11, {}}}}; QTest::addRow("two ansi sequences")