From ff8611248e19d13cb3e952285d6eeb2e269e0a64 Mon Sep 17 00:00:00 2001 From: lievenhey <82457690+lievenhey@users.noreply.github.com> Date: Mon, 12 Dec 2022 08:41:26 +0100 Subject: [PATCH] make syntax highlighting customizeable (#448) fixes #434 --- src/models/disassemblymodel.cpp | 5 +- src/models/disassemblymodel.h | 12 +- src/models/highlighter.cpp | 32 ++--- src/models/highlighter.hpp | 21 +++- src/models/sourcecodemodel.cpp | 5 +- src/models/sourcecodemodel.h | 12 +- src/resultsdisassemblypage.cpp | 60 ++++++++- src/resultsdisassemblypage.h | 8 ++ src/resultsdisassemblypage.ui | 210 ++++++++++++++++++++++++++++---- tests/modeltests/tst_models.cpp | 4 +- 10 files changed, 311 insertions(+), 58 deletions(-) diff --git a/src/models/disassemblymodel.cpp b/src/models/disassemblymodel.cpp index be711a73..f4999b9c 100644 --- a/src/models/disassemblymodel.cpp +++ b/src/models/disassemblymodel.cpp @@ -16,13 +16,12 @@ #include "highlighter.hpp" #include "sourcecodemodel.h" -DisassemblyModel::DisassemblyModel(QObject* parent) +DisassemblyModel::DisassemblyModel(KSyntaxHighlighting::Repository* repository, QObject* parent) : QAbstractTableModel(parent) , m_document(new QTextDocument(this)) - , m_highlighter(new Highlighter(m_document, this)) + , m_highlighter(new Highlighter(m_document, repository, this)) { m_document->setUndoRedoEnabled(false); - m_highlighter->setDefinitionForName(QStringLiteral("GNU Assembler")); } DisassemblyModel::~DisassemblyModel() = default; diff --git a/src/models/disassemblymodel.h b/src/models/disassemblymodel.h index f3ca6cc5..45d2d648 100644 --- a/src/models/disassemblymodel.h +++ b/src/models/disassemblymodel.h @@ -18,11 +18,16 @@ class QTextDocument; class Highlighter; +namespace KSyntaxHighlighting { +class Definition; +class Repository; +} + class DisassemblyModel : public QAbstractTableModel { Q_OBJECT public: - explicit DisassemblyModel(QObject* parent = nullptr); + explicit DisassemblyModel(KSyntaxHighlighting::Repository* repository, QObject* parent = nullptr); ~DisassemblyModel(); void setDisassembly(const DisassemblyOutput& disassemblyOutput, const Data::CallerCalleeResults& results); @@ -39,6 +44,11 @@ class DisassemblyModel : public QAbstractTableModel Data::FileLine fileLineForIndex(const QModelIndex& index) const; QModelIndex indexForFileLine(const Data::FileLine& line) const; + Highlighter* highlighter() const + { + return m_highlighter; + } + enum Columns { AddrColumn, diff --git a/src/models/highlighter.cpp b/src/models/highlighter.cpp index d5e64e62..d008e7a8 100644 --- a/src/models/highlighter.cpp +++ b/src/models/highlighter.cpp @@ -19,13 +19,16 @@ #include #endif -Highlighter::Highlighter(QTextDocument* document, QObject* parent) +Highlighter::Highlighter(QTextDocument* document, KSyntaxHighlighting::Repository* repository, QObject* parent) : QObject(parent) #if KF5SyntaxHighlighting_FOUND - , m_repository(std::make_unique()) , m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(document)) + , m_repository(repository) #endif { +#if !KF5SyntaxHighlighting_FOUND + Q_UNUSED(repository); +#endif document->setDefaultFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); // if qApp is used, UBSAN complains @@ -36,23 +39,18 @@ Highlighter::Highlighter(QTextDocument* document, QObject* parent) Highlighter::~Highlighter() = default; -void Highlighter::setDefinitionForFilename(const QString& filename) +void Highlighter::setDefinition(const KSyntaxHighlighting::Definition& definition) { #if KF5SyntaxHighlighting_FOUND - const auto def = m_repository->definitionForFileName(filename); - m_highlighter->setDefinition(def); -#else - Q_UNUSED(filename); -#endif -} + // don't reparse if definition hasn't changed + if (m_currentDefinition == definition.name()) + return; -void Highlighter::setDefinitionForName(const QString& name) -{ -#if KF5SyntaxHighlighting_FOUND - const auto def = m_repository->definitionForName(name); - m_highlighter->setDefinition(def); + m_highlighter->setDefinition(definition); + m_currentDefinition = definition.name(); + emit definitionChanged(m_currentDefinition); #else - Q_UNUSED(name); + Q_UNUSED(definition); #endif } @@ -68,6 +66,10 @@ bool Highlighter::eventFilter(QObject* /*watched*/, QEvent* event) void Highlighter::updateColorTheme() { #if KF5SyntaxHighlighting_FOUND + if (!m_repository) { + return; + } + KSyntaxHighlighting::Repository::DefaultTheme theme; if (QPalette().base().color().lightness() < 128) { theme = KSyntaxHighlighting::Repository::DarkTheme; diff --git a/src/models/highlighter.hpp b/src/models/highlighter.hpp index c24943bb..a4ef59fd 100644 --- a/src/models/highlighter.hpp +++ b/src/models/highlighter.hpp @@ -17,6 +17,7 @@ class QTextDocument; namespace KSyntaxHighlighting { class SyntaxHighlighter; +class Definition; class Repository; } @@ -24,11 +25,22 @@ class Highlighter : public QObject { Q_OBJECT public: - Highlighter(QTextDocument* document, QObject* parent = nullptr); + Highlighter(QTextDocument* document, KSyntaxHighlighting::Repository* repository, QObject* parent = nullptr); ~Highlighter(); - void setDefinitionForFilename(const QString& filename); - void setDefinitionForName(const QString& name); + void setDefinition(const KSyntaxHighlighting::Definition& definition); + + QString definition() const + { +#if KF5SyntaxHighlighting_FOUND + return m_currentDefinition; +#else + return {}; +#endif + } + +signals: + void definitionChanged(const QString& definition); protected: bool eventFilter(QObject* watched, QEvent* event) override; @@ -37,7 +49,8 @@ class Highlighter : public QObject void updateColorTheme(); #if KF5SyntaxHighlighting_FOUND - std::unique_ptr m_repository; 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 0d4a7f72..2570521f 100644 --- a/src/models/sourcecodemodel.cpp +++ b/src/models/sourcecodemodel.cpp @@ -15,10 +15,10 @@ #include "highlighter.hpp" -SourceCodeModel::SourceCodeModel(QObject* parent) +SourceCodeModel::SourceCodeModel(KSyntaxHighlighting::Repository* repository, QObject* parent) : QAbstractTableModel(parent) , m_document(new QTextDocument(this)) - , m_highlighter(new Highlighter(m_document, this)) + , m_highlighter(new Highlighter(m_document, repository, this)) { m_document->setUndoRedoEnabled(false); qRegisterMetaType(); @@ -64,7 +64,6 @@ void SourceCodeModel::setDisassembly(const DisassemblyOutput& disassemblyOutput, const auto sourceCode = QString::fromUtf8(file.readAll()); m_document->clear(); - m_highlighter->setDefinitionForFilename(disassemblyOutput.mainSourceFileName); m_document->setPlainText(sourceCode); m_document->setTextWidth(m_document->idealWidth()); diff --git a/src/models/sourcecodemodel.h b/src/models/sourcecodemodel.h index 9278518a..4d86e998 100644 --- a/src/models/sourcecodemodel.h +++ b/src/models/sourcecodemodel.h @@ -18,13 +18,18 @@ class QTextDocument; class Highlighter; +namespace KSyntaxHighlighting { +class Repository; +class Definition; +} + Q_DECLARE_METATYPE(QTextLine) class SourceCodeModel : public QAbstractTableModel { Q_OBJECT public: - explicit SourceCodeModel(QObject* parent = nullptr); + explicit SourceCodeModel(KSyntaxHighlighting::Repository* repository, QObject* parent = nullptr); ~SourceCodeModel(); void clear(); @@ -39,6 +44,11 @@ class SourceCodeModel : public QAbstractTableModel Data::FileLine fileLineForIndex(const QModelIndex& index) const; QModelIndex indexForFileLine(const Data::FileLine& line) const; + Highlighter* highlighter() const + { + return m_highlighter; + } + enum Columns { SourceCodeLineNumber, diff --git a/src/resultsdisassemblypage.cpp b/src/resultsdisassemblypage.cpp index 0addff37..6df7c198 100644 --- a/src/resultsdisassemblypage.cpp +++ b/src/resultsdisassemblypage.cpp @@ -28,6 +28,14 @@ #include "parsers/perf/perfparser.h" #include "resultsutil.h" +#if KF5SyntaxHighlighting_FOUND +#include "highlighter.hpp" +#include +#include +#include +#include +#endif + #include "data.h" #include "models/codedelegate.h" #include "models/costdelegate.h" @@ -41,8 +49,14 @@ ResultsDisassemblyPage::ResultsDisassemblyPage(QWidget* parent) : QWidget(parent) , ui(new Ui::ResultsDisassemblyPage) - , m_disassemblyModel(new DisassemblyModel(this)) - , m_sourceCodeModel(new SourceCodeModel(this)) +#if KF5SyntaxHighlighting_FOUND + , m_repository(new KSyntaxHighlighting::Repository) + , m_disassemblyModel(new DisassemblyModel(m_repository.get(), this)) + , m_sourceCodeModel(new SourceCodeModel(m_repository.get(), this)) +#else + , m_disassemblyModel(new DisassemblyModel(nullptr, this)) + , m_sourceCodeModel(new SourceCodeModel(nullptr, this)) +#endif , m_disassemblyCostDelegate(new CostDelegate(DisassemblyModel::CostRole, DisassemblyModel::TotalCostRole, this)) , m_sourceCodeCostDelegate(new CostDelegate(SourceCodeModel::CostRole, SourceCodeModel::TotalCostRole, this)) , m_disassemblyDelegate(new CodeDelegate(DisassemblyModel::RainbowLineNumberRole, DisassemblyModel::HighlightRole, @@ -123,6 +137,42 @@ ResultsDisassemblyPage::ResultsDisassemblyPage(QWidget* parent) } } }); + +#if KF5SyntaxHighlighting_FOUND + QStringList schemes; + + const auto definitions = m_repository->definitions(); + schemes.reserve(definitions.size()); + + std::transform(definitions.begin(), definitions.end(), std::back_inserter(schemes), + [](const auto& definition) { return definition.name(); }); + + auto definitionModel = new QStringListModel(this); + definitionModel->setStringList(schemes); + + auto connectCompletion = [definitionModel, schemes, this](QComboBox* box, auto* model) { + auto completer = new QCompleter(this); + completer->setModel(definitionModel); + completer->setCaseSensitivity(Qt::CaseInsensitive); + completer->setCompletionMode(QCompleter::PopupCompletion); + box->setCompleter(completer); + box->setModel(definitionModel); + box->setCurrentText(model->highlighter()->definition()); + + connect(box, qOverload(&QComboBox::activated), this, [this, model, box]() { + model->highlighter()->setDefinition(m_repository->definitionForName(box->currentText())); + }); + + connect(model->highlighter(), &Highlighter::definitionChanged, + [box](const QString& definition) { box->setCurrentText(definition); }); + }; + + connectCompletion(ui->sourceCodeComboBox, m_sourceCodeModel); + connectCompletion(ui->assemblyComboBox, m_disassemblyModel); +#else + ui->customSourceCodeHighlighting->setVisible(false); + ui->customAssemblyHighlighting->setVisible(false); +#endif } ResultsDisassemblyPage::~ResultsDisassemblyPage() = default; @@ -186,6 +236,12 @@ void ResultsDisassemblyPage::showDisassembly(const DisassemblyOutput& disassembl m_disassemblyModel->clear(); m_sourceCodeModel->clear(); +#if KF5SyntaxHighlighting_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(m_curSymbol); ui->filenameLabel->setText(disassemblyOutput.mainSourceFileName); diff --git a/src/resultsdisassemblypage.h b/src/resultsdisassemblypage.h index 50496f9d..a04482fa 100644 --- a/src/resultsdisassemblypage.h +++ b/src/resultsdisassemblypage.h @@ -9,6 +9,7 @@ #pragma once #include "data.h" +#include "hotspot-config.h" #include "models/costdelegate.h" #include @@ -34,6 +35,10 @@ struct DisassemblyOutput; class DisassemblyModel; class SourceCodeModel; +namespace KSyntaxHighlighting { +class Repository; +} + class ResultsDisassemblyPage : public QWidget { Q_OBJECT @@ -58,6 +63,9 @@ class ResultsDisassemblyPage : public QWidget void showDisassembly(const DisassemblyOutput& disassemblyOutput); QScopedPointer ui; +#if KF5SyntaxHighlighting_FOUND + QScopedPointer m_repository; +#endif // Model DisassemblyModel* m_disassemblyModel; SourceCodeModel* m_sourceCodeModel; diff --git a/src/resultsdisassemblypage.ui b/src/resultsdisassemblypage.ui index 68a572f5..0dc6fa09 100644 --- a/src/resultsdisassemblypage.ui +++ b/src/resultsdisassemblypage.ui @@ -44,38 +44,194 @@ - - - Qt::Horizontal - - - - true - - - false + + + + 0 - - true + + 0 - - 200 - - - - - true + + 0 - - false + + 0 - - true + + 0 - - 200 - - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + false + + + true + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Syntax Highlighting: + + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + false + + + true + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Syntax Highlighting: + + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/tests/modeltests/tst_models.cpp b/tests/modeltests/tst_models.cpp index 2e41308a..061e0f99 100644 --- a/tests/modeltests/tst_models.cpp +++ b/tests/modeltests/tst_models.cpp @@ -345,7 +345,7 @@ private slots: locationCost.inclusiveCost[0] += 200; locationCost.selfCost[0] += 200; - DisassemblyModel model; + DisassemblyModel model(nullptr); // no disassembly data yet QCOMPARE(model.columnCount(), DisassemblyModel::COLUMN_COUNT); @@ -385,7 +385,7 @@ private slots: Data::CallerCalleeResults results; Data::callerCalleesFromBottomUpData(tree, &results); - SourceCodeModel model; + SourceCodeModel model(nullptr); QCOMPARE(model.columnCount(), SourceCodeModel::COLUMN_COUNT); QCOMPARE(model.rowCount(), 0);