From 058aa799fef19e7eda2066f0e3e966e375a8c730 Mon Sep 17 00:00:00 2001 From: Lieven Hey Date: Mon, 23 Oct 2023 18:08:28 +0200 Subject: [PATCH] use QTextLayout for resultsdisaasemblypage This patch replaces the usage of QTextDocument in the sourcecode and disassembly view. QTextDocument is way to overkill for that job. This patch also includes an highlighter based on ansi codes, which can be used instead of KSyntaxHighlighting for objdump (if supported). --- src/models/CMakeLists.txt | 3 +- src/models/disassemblymodel.cpp | 39 ++--- src/models/disassemblymodel.h | 11 +- src/models/disassemblyoutput.cpp | 33 +++- src/models/formattingutils.cpp | 24 +++ src/models/formattingutils.h | 14 ++ src/models/highlightedtext.cpp | 259 +++++++++++++++++++++++++++++++ src/models/highlightedtext.h | 59 +++++++ src/models/highlighter.cpp | 82 ---------- src/models/highlighter.hpp | 56 ------- src/models/sourcecodemodel.cpp | 49 ++---- src/models/sourcecodemodel.h | 21 +-- src/resultsdisassemblypage.cpp | 24 +-- 13 files changed, 439 insertions(+), 235 deletions(-) create mode 100644 src/models/formattingutils.cpp create mode 100644 src/models/formattingutils.h create mode 100644 src/models/highlightedtext.cpp create mode 100644 src/models/highlightedtext.h delete mode 100644 src/models/highlighter.cpp delete mode 100644 src/models/highlighter.hpp diff --git a/src/models/CMakeLists.txt b/src/models/CMakeLists.txt index e0ab27021..fc7f1c7aa 100644 --- a/src/models/CMakeLists.txt +++ b/src/models/CMakeLists.txt @@ -11,8 +11,9 @@ add_library( disassemblyoutput.cpp eventmodel.cpp filterandzoomstack.cpp + formattingutils.cpp frequencymodel.cpp - highlighter.cpp + highlightedtext.cpp processfiltermodel.cpp processlist_unix.cpp processmodel.cpp diff --git a/src/models/disassemblymodel.cpp b/src/models/disassemblymodel.cpp index 6d2a58e25..58d8bcfa4 100644 --- a/src/models/disassemblymodel.cpp +++ b/src/models/disassemblymodel.cpp @@ -6,22 +6,15 @@ SPDX-License-Identifier: GPL-2.0-or-later */ -#include -#include -#include - #include "disassemblymodel.h" -#include "highlighter.hpp" #include "search.h" #include "sourcecodemodel.h" DisassemblyModel::DisassemblyModel(KSyntaxHighlighting::Repository* repository, QObject* parent) : QAbstractTableModel(parent) - , m_document(new QTextDocument(this)) - , m_highlighter(new Highlighter(m_document, repository, this)) + , m_highlightedText(repository) { - m_document->setUndoRedoEnabled(false); } DisassemblyModel::~DisassemblyModel() = default; @@ -56,17 +49,13 @@ void DisassemblyModel::setDisassembly(const DisassemblyOutput& disassemblyOutput m_results = results; m_numTypes = results.selfCosts.numTypes(); - m_document->clear(); - - QTextCursor cursor(m_document); - cursor.beginEditBlock(); - for (const auto& it : disassemblyOutput.disassemblyLines) { - cursor.insertText(it.disassembly); - cursor.insertBlock(); - } - cursor.endEditBlock(); + QStringList assemblyLines; + assemblyLines.reserve(disassemblyOutput.disassemblyLines.size()); + std::transform(disassemblyOutput.disassemblyLines.cbegin(), disassemblyOutput.disassemblyLines.cend(), + std::back_inserter(assemblyLines), + [](const DisassemblyOutput::DisassemblyLine& line) { return line.disassembly; }); - m_document->setTextWidth(m_document->idealWidth()); + m_highlightedText.setText(assemblyLines); endResetModel(); } @@ -110,6 +99,7 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const } const auto& data = m_data.disassemblyLines.at(index.row()); + const auto& line = m_highlightedText.textAt(index.row()); if (role == AddrRole) return data.addr; @@ -123,10 +113,10 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const } else if (index.column() == BranchColumn) { return data.branchVisualisation; } else if (index.column() == DisassemblyColumn) { - const auto block = m_document->findBlockByLineNumber(index.row()); - if (role == SyntaxHighlightRole) - return QVariant::fromValue(block.layout()->lineAt(0)); - return block.text(); + if (role == SyntaxHighlightRole) { + return QVariant::fromValue(m_highlightedText.lineAt(index.row())); + } + return m_highlightedText.textAt(index.row()); } } @@ -149,7 +139,7 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const return totalCost; } else if (role == Qt::ToolTipRole) { auto tooltip = tr("addr: %1
assembly: %2
disassembly: %3") - .arg(QString::number(data.addr, 16), data.disassembly); + .arg(QString::number(data.addr, 16), line); return Util::formatTooltip(tooltip, locationCost, m_results.selfCosts); } @@ -158,8 +148,7 @@ QVariant DisassemblyModel::data(const QModelIndex& index, int role) const return Util::formatCostRelative(costLine, totalCost, true); } else { if (role == Qt::ToolTipRole) - return tr("%1
No samples at this location.
") - .arg(data.disassembly.toHtmlEscaped()); + return tr("%1
No samples at this location.
").arg(line.toHtmlEscaped()); else return QString(); } diff --git a/src/models/disassemblymodel.h b/src/models/disassemblymodel.h index 6b1be8fea..83853a378 100644 --- a/src/models/disassemblymodel.h +++ b/src/models/disassemblymodel.h @@ -14,9 +14,7 @@ #include "data.h" #include "disassemblyoutput.h" - -class QTextDocument; -class Highlighter; +#include "highlightedtext.h" namespace KSyntaxHighlighting { class Definition; @@ -47,9 +45,9 @@ class DisassemblyModel : public QAbstractTableModel Data::FileLine fileLineForIndex(const QModelIndex& index) const; QModelIndex indexForFileLine(const Data::FileLine& line) const; - Highlighter* highlighter() const + HighlightedText* highlightedText() { - return m_highlighter; + return &m_highlightedText; } enum Columns @@ -81,8 +79,7 @@ public slots: void find(const QString& search, Direction direction, int offset); private: - QTextDocument* m_document; - Highlighter* m_highlighter; + HighlightedText m_highlightedText; DisassemblyOutput m_data; Data::CallerCalleeResults m_results; int m_numTypes = 0; diff --git a/src/models/disassemblyoutput.cpp b/src/models/disassemblyoutput.cpp index 19ad25a86..e9d5559ee 100644 --- a/src/models/disassemblyoutput.cpp +++ b/src/models/disassemblyoutput.cpp @@ -17,28 +17,46 @@ #include #include +#include "formattingutils.h" + namespace { Q_LOGGING_CATEGORY(disassemblyoutput, "hotspot.disassemblyoutput") -bool canVisualizeJumps(const QString& objdump) +QString objdumpHelp(const QString& objdump) { QProcess process; process.setProcessChannelMode(QProcess::ForwardedErrorChannel); process.start(objdump, {QStringLiteral("-H")}); if (!process.waitForFinished(1000)) { qCWarning(disassemblyoutput) << "failed to query objdump help output:" << objdump << process.errorString(); - return false; + return {}; } const auto help = process.readAllStandardOutput(); - return help.contains("--visualize-jumps"); + return QString::fromUtf8(help); +} + +bool canVisualizeJumps(const QString& objdump) +{ + return objdumpHelp(objdump).contains(QStringLiteral("--visualize-jumps")); +} + +bool canUseSyntaxHighlighting(const QString& objdump) +{ + return objdumpHelp(objdump).contains(QStringLiteral("--disassembler-color")); } -DisassemblyOutput::LinkedFunction extractLinkedFunction(const QString& disassembly) +DisassemblyOutput::LinkedFunction extractLinkedFunction(const QString& disassemblyWithAnsi) { DisassemblyOutput::LinkedFunction function = {}; + const auto escapeChar = QLatin1Char('\u001B'); + + const auto disassembly = + disassemblyWithAnsi.contains(escapeChar) ? utils::removeAnsi(disassemblyWithAnsi) : disassemblyWithAnsi; + const auto leftBracketIndex = disassembly.indexOf(QLatin1Char('<')); const auto rightBracketIndex = disassembly.indexOf(QLatin1Char('>')); + if (leftBracketIndex != -1 && rightBracketIndex != -1) { if (leftBracketIndex < rightBracketIndex) { function.name = disassembly.mid(leftBracketIndex + 1, rightBracketIndex - leftBracketIndex - 1); @@ -61,6 +79,7 @@ DisassemblyOutput::LinkedFunction extractLinkedFunction(const QString& disassemb } } } + return function; } @@ -300,6 +319,12 @@ DisassemblyOutput DisassemblyOutput::disassemble(const QString& objdump, const Q else qCInfo(disassemblyoutput) << "objdump binary does not support `--visualize-jumps`:" << processPath; + if (canUseSyntaxHighlighting(processPath)) { + arguments.append(QStringLiteral("--disassembler-color=color")); + } else { + qCInfo(disassemblyoutput) << "objdump binary does not support `--disassembler-color`:" << processPath; + } + auto binary = findBinaryForSymbol(debugPaths, extraLibPaths, symbol); if (binary.isEmpty()) { disassemblyOutput.errorMessage += diff --git a/src/models/formattingutils.cpp b/src/models/formattingutils.cpp new file mode 100644 index 000000000..f7e6f836c --- /dev/null +++ b/src/models/formattingutils.cpp @@ -0,0 +1,24 @@ +/* + SPDX-FileCopyrightText: Lieven Hey + SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "formattingutils.h" + +QString utils::removeAnsi(const QString& stringWithAnsi) +{ + const static QChar escapeChar = QLatin1Char('\u001B'); + if (!stringWithAnsi.contains(escapeChar)) { + return stringWithAnsi; + } + + QString ansiFreeString = stringWithAnsi; + while (ansiFreeString.contains(escapeChar)) { + const auto escapeStart = ansiFreeString.indexOf(escapeChar); + const auto escapeEnd = ansiFreeString.indexOf(QLatin1Char('m'), escapeStart); + ansiFreeString.remove(escapeStart, escapeEnd - escapeStart + 1); + } + return ansiFreeString; +} diff --git a/src/models/formattingutils.h b/src/models/formattingutils.h new file mode 100644 index 000000000..9af4dc91b --- /dev/null +++ b/src/models/formattingutils.h @@ -0,0 +1,14 @@ +/* + SPDX-FileCopyrightText: Lieven Hey + SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include + +namespace utils { +QString removeAnsi(const QString& stringWithAnsi); +} diff --git a/src/models/highlightedtext.cpp b/src/models/highlightedtext.cpp new file mode 100644 index 000000000..a2f2bff05 --- /dev/null +++ b/src/models/highlightedtext.cpp @@ -0,0 +1,259 @@ +/* + SPDX-FileCopyrightText: Lieven Hey + SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "highlightedtext.h" + +#include + +#include + +#include +#if KFSyntaxHighlighting_FOUND +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "formattingutils.h" + +namespace { +#if KFSyntaxHighlighting_FOUND +class KSyntaxHighlightingImplementation : public HighlightingImplementation, + public KSyntaxHighlighting::AbstractHighlighter +{ +public: + KSyntaxHighlightingImplementation(KSyntaxHighlighting::Repository* repository) + : m_repository(repository) + { + } + ~KSyntaxHighlightingImplementation() override = default; + + QVector format(const QStringList& text) override + { + m_formats.clear(); + m_offset = 0; + + KSyntaxHighlighting::State state; + for (const auto& line : text) { + state = highlightLine(line, state); + + // KSyntaxHighlighting uses line offsets but QTextLayout uses global offsets + // the +1 is for the newline at the end of the line + m_offset += line.size() + 1; + } + + return m_formats; + } + + void themeChanged() override + { + if (!m_repository) { + return; + } + + KSyntaxHighlighting::Repository::DefaultTheme theme; + if (QPalette().base().color().lightness() < 128) { + theme = KSyntaxHighlighting::Repository::DarkTheme; + } else { + theme = KSyntaxHighlighting::Repository::LightTheme; + } + setTheme(m_repository->defaultTheme(theme)); + } + + void setHighlightingDefinition(const KSyntaxHighlighting::Definition& definition) override + { + setDefinition(definition); + } + +protected: + void applyFormat(int offset, int length, const KSyntaxHighlighting::Format& format) override + { + QTextCharFormat textCharFormat; + textCharFormat.setForeground(format.textColor(theme())); + m_formats.push_back({m_offset + offset, length, textCharFormat}); + } + +private: + KSyntaxHighlighting::Repository* m_repository; + QVector m_formats; + int m_offset = 0; +}; +#else +class KSyntaxHighlightingImplementation : public HighlightingImplementation +{ +public: + KSyntaxHighlightingImplementation(KSyntaxHighlighting::Repository*) = default; + ~KSyntaxHighlightingImplementation() override = default; + + QVector format(const QStringList& text) override + { + return {}; + } + + void themeChanged() override { } + + void setHighlightingDefinition(const KSyntaxHighlighting::Definition& /*definition*/) override { } +}; +#endif + +class AnsiHighlightingImplementation : public HighlightingImplementation +{ +public: + AnsiHighlightingImplementation() = default; + ~AnsiHighlightingImplementation() override = default; + + QVector format(const QStringList& text) override + { + QVector formats; + + int offset = 0; + QTextLayout::FormatRange format; + + for (const auto& line : text) { + for (auto c = line.cbegin(); c != line.cend(); c++) { + if (*c == QLatin1Char('\u001B')) { + // the strings log like this: + // \e[33m - set color + // \e[0m - clear color + // so we can grab the next two chars and check if we can convert them into a number + // if not -> clear format + + std::advance(c, 1); + Q_ASSERT(*c == QLatin1Char('[')); + + auto color = line.mid(std::distance(line.cbegin(), c) + 1, 2); + bool ok = false; + uint8_t colorCode = color.toUInt(&ok); + if (ok) { + // only support the 8 default colors + Q_ASSERT(colorCode >= 30 && colorCode <= 37); + + format.start = offset; + const auto colorRole = static_cast(colorCode - 30); + format.format.setForeground(m_colorScheme.foreground(colorRole)); + + std::advance(c, 3); + + // make sure that there are no additional codes + Q_ASSERT(*c == QLatin1Char('m')); + } else { + // make sure we have a reset sequence + Q_ASSERT(color == QStringLiteral("0m")); + format.length = offset - format.start; + formats.push_back(format); + + std::advance(c, 2); + } + } else { + offset++; + } + } + offset++; + } + + return formats; + } + + void themeChanged() override + { + m_colorScheme = KColorScheme(QPalette::Normal, KColorScheme::Complementary); + } + + void setHighlightingDefinition(const KSyntaxHighlighting::Definition& /*definition*/) override + { // not used since we use ansi sequences for highlighting + } + +private: + KColorScheme m_colorScheme; +}; +} + +HighlightedText::HighlightedText(KSyntaxHighlighting::Repository* repository, QObject* parent) + : QObject(parent) +#if KFSyntaxHighlighting_FOUND + , m_repository(repository) +#endif + , m_layout(std::make_unique()) +{ + m_layout->setCacheEnabled(true); + m_layout->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); +} + +HighlightedText::~HighlightedText() = default; + +void HighlightedText::setText(const QStringList& text) +{ + bool usesAnsi = std::any_of(text.cbegin(), text.cend(), + [](const QString& line) { return line.contains(QLatin1Char('\u001B')); }); + + if (usesAnsi) { + m_highlighter = std::make_unique(); + } else { + m_highlighter = std::make_unique(m_repository); + } + + m_highlighter->themeChanged(); + m_highlighter->format(text); + + m_lines.clear(); + + QString formattedText; + + for (const auto& line : text) { + const auto& ansiFreeLine = utils::removeAnsi(line); + m_lines.push_back(line); + const auto& lineWithNewline = QLatin1String("%1%2").arg(ansiFreeLine, QChar::LineSeparator); + formattedText += lineWithNewline; + } + + m_layout->setText(formattedText); + + applyFormatting(); +} + +void HighlightedText::setDefinition(const KSyntaxHighlighting::Definition& definition) +{ + Q_ASSERT(m_highlighter); + m_highlighter->setHighlightingDefinition(definition); + applyFormatting(); +} + +QString HighlightedText::textAt(int index) const +{ + Q_ASSERT(m_highlighter); + Q_ASSERT(index < m_lines.size()); + return m_lines.at(index); +} + +QTextLine HighlightedText::lineAt(int index) const +{ + Q_ASSERT(m_layout); + return m_layout->lineAt(index); +} + +void HighlightedText::applyFormatting() +{ + Q_ASSERT(m_highlighter); + + m_layout->setFormats(m_highlighter->format(m_lines)); + + m_layout->clearLayout(); + m_layout->beginLayout(); + + while (true) { + QTextLine line = m_layout->createLine(); + if (!line.isValid()) + break; + + line.setPosition(QPointF(0, 0)); + } + m_layout->endLayout(); +} diff --git a/src/models/highlightedtext.h b/src/models/highlightedtext.h new file mode 100644 index 000000000..c614e8a47 --- /dev/null +++ b/src/models/highlightedtext.h @@ -0,0 +1,59 @@ +/* + SPDX-FileCopyrightText: Lieven Hey + SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include +#include +#include + +#include "hotspot-config.h" + +namespace KSyntaxHighlighting { +class SyntaxHighlighter; +class Definition; +class Repository; +} + +class HighlightingImplementation +{ + Q_DISABLE_COPY(HighlightingImplementation) +public: + HighlightingImplementation() = default; + virtual ~HighlightingImplementation() = default; + + virtual QVector format(const QStringList& text) = 0; + virtual void themeChanged() = 0; + virtual void setHighlightingDefinition(const KSyntaxHighlighting::Definition& definition) = 0; +}; + +class HighlightedText : public QObject +{ + Q_OBJECT +public: + HighlightedText(KSyntaxHighlighting::Repository* repository, QObject* parent = nullptr); + ~HighlightedText() override; + + void setText(const QStringList& text); + void setDefinition(const KSyntaxHighlighting::Definition& definition); + + QString textAt(int index) const; + QTextLine lineAt(int index) const; + +private slots: + void applyFormatting(); + +private: + void updateHighlighting(); + +#if KFSyntaxHighlighting_FOUND + KSyntaxHighlighting::Repository* m_repository; +#endif + std::unique_ptr m_highlighter; + std::unique_ptr m_layout; + QStringList m_lines; +}; diff --git a/src/models/highlighter.cpp b/src/models/highlighter.cpp deleted file mode 100644 index f8e8241cd..000000000 --- a/src/models/highlighter.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - SPDX-FileCopyrightText: Lieven Hey - SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#include "highlighter.hpp" - -#include -#include -#include -#include - -#if KFSyntaxHighlighting_FOUND -#include -#include -#include -#include -#endif - -Highlighter::Highlighter(QTextDocument* document, KSyntaxHighlighting::Repository* repository, QObject* parent) - : QObject(parent) -#if KFSyntaxHighlighting_FOUND - , m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(document)) - , m_repository(repository) -#endif -{ -#if !KFSyntaxHighlighting_FOUND - Q_UNUSED(repository); -#endif - document->setDefaultFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - - // if qApp is used, UBSAN complains - QApplication::instance()->installEventFilter(this); - - updateColorTheme(); -} - -Highlighter::~Highlighter() = default; - -void Highlighter::setDefinition(const KSyntaxHighlighting::Definition& definition) -{ -#if KFSyntaxHighlighting_FOUND - // don't reparse if definition hasn't changed - if (m_currentDefinition == definition.name()) - return; - - m_highlighter->setDefinition(definition); - m_currentDefinition = definition.name(); - emit definitionChanged(m_currentDefinition); -#else - Q_UNUSED(definition); -#endif -} - -bool Highlighter::eventFilter(QObject* /*watched*/, QEvent* event) -{ - if (event->type() == QEvent::Type::ApplicationPaletteChange) { - updateColorTheme(); - } - - return false; -} - -void Highlighter::updateColorTheme() -{ -#if KFSyntaxHighlighting_FOUND - if (!m_repository) { - return; - } - - KSyntaxHighlighting::Repository::DefaultTheme theme; - if (QPalette().base().color().lightness() < 128) { - theme = KSyntaxHighlighting::Repository::DarkTheme; - } else { - theme = KSyntaxHighlighting::Repository::LightTheme; - } - m_highlighter->setTheme(m_repository->defaultTheme(theme)); - m_highlighter->rehighlight(); -#endif -} diff --git a/src/models/highlighter.hpp b/src/models/highlighter.hpp deleted file mode 100644 index 3966497a3..000000000 --- a/src/models/highlighter.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - SPDX-FileCopyrightText: Lieven Hey - SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#pragma once - -#include "hotspot-config.h" - -#include - -#include - -class QTextDocument; - -namespace KSyntaxHighlighting { -class SyntaxHighlighter; -class Definition; -class Repository; -} - -class Highlighter : public QObject -{ - Q_OBJECT -public: - Highlighter(QTextDocument* document, KSyntaxHighlighting::Repository* repository, QObject* parent = nullptr); - ~Highlighter(); - - void setDefinition(const KSyntaxHighlighting::Definition& definition); - - QString definition() const - { -#if KFSyntaxHighlighting_FOUND - return m_currentDefinition; -#else - return {}; -#endif - } - -signals: - void definitionChanged(const QString& definition); - -protected: - bool eventFilter(QObject* watched, QEvent* event) override; - -private: - void updateColorTheme(); - -#if KFSyntaxHighlighting_FOUND - KSyntaxHighlighting::SyntaxHighlighter* m_highlighter = nullptr; - KSyntaxHighlighting::Repository* m_repository = nullptr; - QString m_currentDefinition; -#endif -}; diff --git a/src/models/sourcecodemodel.cpp b/src/models/sourcecodemodel.cpp index 5bf71eb88..b7b7e1d3a 100644 --- a/src/models/sourcecodemodel.cpp +++ b/src/models/sourcecodemodel.cpp @@ -18,7 +18,6 @@ #include #include -#include "highlighter.hpp" #include "search.h" #include @@ -26,10 +25,8 @@ Q_LOGGING_CATEGORY(sourcecodemodel, "hotspot.sourcecodemodel", QtWarningMsg) SourceCodeModel::SourceCodeModel(KSyntaxHighlighting::Repository* repository, QObject* parent) : QAbstractTableModel(parent) - , m_document(new QTextDocument(this)) - , m_highlighter(new Highlighter(m_document, repository, this)) + , m_highlightedText(repository) { - m_document->setUndoRedoEnabled(false); qRegisterMetaType(); } @@ -38,7 +35,7 @@ SourceCodeModel::~SourceCodeModel() = default; void SourceCodeModel::clear() { beginResetModel(); - m_document->clear(); + m_highlightedText.setText({}); endResetModel(); } @@ -56,19 +53,6 @@ void SourceCodeModel::setDisassembly(const DisassemblyOutput& disassemblyOutput, return; } - QFile file(disassemblyOutput.realSourceFileName); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - return; - } - - const auto sourceCode = QString::fromUtf8(file.readAll()); - - m_sourceCodeLines.clear(); - m_document->clear(); - - m_document->setPlainText(sourceCode); - m_document->setTextWidth(m_document->idealWidth()); - int maxLineNumber = 0; int minLineNumber = std::numeric_limits::max(); @@ -123,15 +107,16 @@ void SourceCodeModel::setDisassembly(const DisassemblyOutput& disassemblyOutput, m_startLine = minLineNumber - 1; // convert to index m_numLines = maxLineNumber - minLineNumber + 1; // include minLineNumber - m_sourceCodeLines.reserve(m_numLines); + QFile file(disassemblyOutput.realSourceFileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return; + } - for (int i = m_startLine; i < m_startLine + m_numLines; i++) { - auto block = m_document->findBlockByLineNumber(i); - if (!block.isValid()) - continue; + const auto sourceCode = QString::fromUtf8(file.readAll()); - m_sourceCodeLines.push_back({block.text(), block.layout()->lineAt(0)}); - } + m_lines = sourceCode.split(QLatin1Char('\n')); + + m_highlightedText.setText(m_lines); } QVariant SourceCodeModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -183,9 +168,11 @@ QVariant SourceCodeModel::data(const QModelIndex& index, int role) const return m_prettySymbol; } - if (role == SyntaxHighlightRole) - return QVariant::fromValue(m_sourceCodeLines[index.row() - 1].line); - return m_sourceCodeLines[index.row() - 1].text; + int lineNumber = m_startLine + index.row() - 1; + if (role == SyntaxHighlightRole) { + return QVariant::fromValue(m_highlightedText.lineAt(lineNumber)); + } + return m_highlightedText.textAt(lineNumber); } if (index.column() == SourceCodeLineNumber) { @@ -264,13 +251,11 @@ void SourceCodeModel::setSysroot(const QString& sysroot) void SourceCodeModel::find(const QString& search, Direction direction, int current) { - auto searchFunc = [&search](const SourceCodeLine& line) { - return line.text.indexOf(search, 0, Qt::CaseInsensitive) != -1; - }; + auto searchFunc = [&search](const QString& line) { return line.indexOf(search, 0, Qt::CaseInsensitive) != -1; }; auto endReached = [this] { emit searchEndReached(); }; - const int resultIndex = ::search(m_sourceCodeLines, current, direction, searchFunc, endReached); + const int resultIndex = ::search(m_lines, current, direction, searchFunc, endReached); if (resultIndex >= 0) { emit resultFound(createIndex(resultIndex + 1, SourceCodeColumn)); diff --git a/src/models/sourcecodemodel.h b/src/models/sourcecodemodel.h index 4aa3e0bd4..994ad9332 100644 --- a/src/models/sourcecodemodel.h +++ b/src/models/sourcecodemodel.h @@ -9,27 +9,17 @@ #include "data.h" #include "disassemblyoutput.h" +#include "highlightedtext.h" #include #include #include -class QTextDocument; - -class Highlighter; - namespace KSyntaxHighlighting { class Repository; class Definition; } -struct SourceCodeLine -{ - QString text; - QTextLine line; -}; -Q_DECLARE_TYPEINFO(SourceCodeLine, Q_MOVABLE_TYPE); - enum class Direction; Q_DECLARE_METATYPE(QTextLine) @@ -53,9 +43,9 @@ class SourceCodeModel : public QAbstractTableModel Data::FileLine fileLineForIndex(const QModelIndex& index) const; QModelIndex indexForFileLine(const Data::FileLine& line) const; - Highlighter* highlighter() const + HighlightedText* highlightedText() { - return m_highlighter; + return &m_highlightedText; } enum Columns @@ -88,14 +78,13 @@ public slots: private: QString m_sysroot; QSet m_validLineNumbers; - QTextDocument* m_document = nullptr; - QVector m_sourceCodeLines; - Highlighter* m_highlighter = nullptr; + HighlightedText m_highlightedText; Data::Costs m_selfCosts; Data::Costs m_inclusiveCosts; QString m_mainSourceFileName; QString m_prettySymbol; int m_startLine = 0; int m_numLines = 0; + QStringList m_lines; int m_highlightLine = 0; }; diff --git a/src/resultsdisassemblypage.cpp b/src/resultsdisassemblypage.cpp index 8f98db35c..11436a0e7 100644 --- a/src/resultsdisassemblypage.cpp +++ b/src/resultsdisassemblypage.cpp @@ -32,7 +32,6 @@ #include "resultsutil.h" #if KFSyntaxHighlighting_FOUND -#include "highlighter.hpp" #include #include #include @@ -435,14 +434,14 @@ ResultsDisassemblyPage::ResultsDisassemblyPage(CostContextMenu* costContextMenu, completer->setCompletionMode(QCompleter::PopupCompletion); box->setCompleter(completer); box->setModel(definitionModel); - box->setCurrentText(model->highlighter()->definition()); + // box->setCurrentText(model->highlighter()->definition()); connect(box, qOverload(&QComboBox::activated), this, [this, model, box]() { - model->highlighter()->setDefinition(m_repository->definitionForName(box->currentText())); + model->highlightedText()->setDefinition(m_repository->definitionForName(box->currentText())); }); - - connect(model->highlighter(), &Highlighter::definitionChanged, - [box](const QString& definition) { box->setCurrentText(definition); }); + // TODO: this + // connect(model->highlightedText(), &HighlightedText::definitionChanged, + // [box](const QString& definition) { box->setCurrentText(definition); }); }; connectCompletion(ui->sourceCodeComboBox, m_sourceCodeModel); @@ -533,12 +532,6 @@ void ResultsDisassemblyPage::showDisassembly(const DisassemblyOutput& disassembl Q_ASSERT(!m_symbolStack.isEmpty()); const auto& curSymbol = m_symbolStack[m_stackIndex]; -#if KFSyntaxHighlighting_FOUND - m_sourceCodeModel->highlighter()->setDefinition( - m_repository->definitionForFileName(disassemblyOutput.mainSourceFileName)); - m_disassemblyModel->highlighter()->setDefinition(m_repository->definitionForName(QStringLiteral("GNU Assembler"))); -#endif - const auto& entry = m_callerCalleeResults.entry(curSymbol); ui->filenameLabel->setText(disassemblyOutput.mainSourceFileName); @@ -557,6 +550,13 @@ void ResultsDisassemblyPage::showDisassembly(const DisassemblyOutput& disassembl m_disassemblyModel->setDisassembly(disassemblyOutput, m_callerCalleeResults); m_sourceCodeModel->setDisassembly(disassemblyOutput, m_callerCalleeResults); +#if KFSyntaxHighlighting_FOUND + m_sourceCodeModel->highlightedText()->setDefinition( + m_repository->definitionForFileName(disassemblyOutput.mainSourceFileName)); + m_disassemblyModel->highlightedText()->setDefinition( + m_repository->definitionForName(QStringLiteral("GNU Assembler"))); +#endif + ResultsUtil::hideEmptyColumns(m_callerCalleeResults.selfCosts, ui->assemblyView, DisassemblyModel::COLUMN_COUNT); ResultsUtil::hideEmptyColumns(m_callerCalleeResults.selfCosts, ui->sourceCodeView, SourceCodeModel::COLUMN_COUNT);