diff --git a/src/models/disassemblymodel.cpp b/src/models/disassemblymodel.cpp index f4999b9c..015cd62c 100644 --- a/src/models/disassemblymodel.cpp +++ b/src/models/disassemblymodel.cpp @@ -14,6 +14,7 @@ #include "hotspot-config.h" #include "highlighter.hpp" +#include "search.h" #include "sourcecodemodel.h" DisassemblyModel::DisassemblyModel(KSyntaxHighlighting::Repository* repository, QObject* parent) @@ -222,3 +223,19 @@ QModelIndex DisassemblyModel::indexForFileLine(const Data::FileLine& fileLine) c return {}; return index(bestMatch, 0); } + +void DisassemblyModel::find(const QString& search, Direction direction, int offset) +{ + auto searchFunc = [&search](const DisassemblyOutput::DisassemblyLine& line) { + return line.disassembly.indexOf(search, 0, Qt::CaseInsensitive) != -1; + }; + + int resultIndex = ::search( + m_data.disassemblyLines, searchFunc, [this] { emit resultFound({}); }, direction, offset); + + if (resultIndex >= 0) { + emit resultFound(createIndex(resultIndex, DisassemblyColumn)); + } else { + emit resultFound({}); + } +} diff --git a/src/models/disassemblymodel.h b/src/models/disassemblymodel.h index 45d2d648..1da6f1db 100644 --- a/src/models/disassemblymodel.h +++ b/src/models/disassemblymodel.h @@ -23,6 +23,8 @@ class Definition; class Repository; } +enum class Direction; + class DisassemblyModel : public QAbstractTableModel { Q_OBJECT @@ -67,8 +69,13 @@ class DisassemblyModel : public QAbstractTableModel SyntaxHighlightRole, }; +signals: + void resultFound(QModelIndex index); + void searchEndReached(); + public slots: void updateHighlighting(int line); + void find(const QString& search, Direction direction, int offset); private: QTextDocument* m_document; diff --git a/src/models/search.h b/src/models/search.h new file mode 100644 index 00000000..380d8091 --- /dev/null +++ b/src/models/search.h @@ -0,0 +1,50 @@ +/* + 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 + +enum class Direction +{ + Forward, + Backward +}; + +template +int search(QVector source, SearchFunc&& searchFunc, EndReached&& endReached, Direction direction, int offset) +{ + if (offset > source.size() || offset < 0) { + offset = 0; + } + + auto start = direction == Direction::Forward + ? (source.begin() + offset) + : (source.end() - (source.size() - offset - 1)); // 1 one due to offset of the reverse iterator + + auto it = direction == Direction::Forward + ? std::find_if(start, source.end(), searchFunc) + : (std::find_if(std::make_reverse_iterator(start), source.rend(), searchFunc) + 1).base(); + + // it is less than source.begin() if the backward search ends at source.rend() + if (it >= source.begin() && it < source.end()) { + auto distance = std::distance(source.begin(), it); + return distance; + } + + it = direction == Direction::Forward + ? std::find_if(source.begin(), start, searchFunc) + : (std::find_if(source.rbegin(), std::make_reverse_iterator(start), searchFunc) + 1).base(); + + if (it != source.end()) { + auto distance = std::distance(source.begin(), it); + endReached(); + return distance; + } + + return -1; +} diff --git a/src/models/sourcecodemodel.cpp b/src/models/sourcecodemodel.cpp index 098bae56..29a6cd00 100644 --- a/src/models/sourcecodemodel.cpp +++ b/src/models/sourcecodemodel.cpp @@ -13,7 +13,10 @@ #include #include +#include + #include "highlighter.hpp" +#include "search.h" SourceCodeModel::SourceCodeModel(KSyntaxHighlighting::Repository* repository, QObject* parent) : QAbstractTableModel(parent) @@ -244,3 +247,19 @@ void SourceCodeModel::setSysroot(const QString& sysroot) { m_sysroot = sysroot; } + +void SourceCodeModel::find(const QString& search, Direction direction, int offset) +{ + auto searchFunc = [&search](const SourceCodeLine& line) { + return line.text.indexOf(search, 0, Qt::CaseInsensitive) != -1; + }; + + int resultIndex = ::search( + m_sourceCodeLines, searchFunc, [this] { emit resultFound({}); }, direction, offset); + + if (resultIndex >= 0) { + emit resultFound(createIndex(resultIndex + 1, SourceCodeColumn)); + } else { + emit resultFound({}); + } +} diff --git a/src/models/sourcecodemodel.h b/src/models/sourcecodemodel.h index 8039b28e..14412159 100644 --- a/src/models/sourcecodemodel.h +++ b/src/models/sourcecodemodel.h @@ -29,6 +29,8 @@ struct SourceCodeLine QTextLine line; }; +enum class Direction; + Q_DECLARE_METATYPE(QTextLine) class SourceCodeModel : public QAbstractTableModel @@ -72,10 +74,16 @@ class SourceCodeModel : public QAbstractTableModel FileLineRole, }; +signals: + void resultFound(QModelIndex index); + void searchEndReached(); + public slots: void updateHighlighting(int line); void setSysroot(const QString& sysroot); + void find(const QString& search, Direction direction, int offset); + private: QString m_sysroot; QSet m_validLineNumbers; diff --git a/src/resultsdisassemblypage.cpp b/src/resultsdisassemblypage.cpp index 51a1f80f..07dba97b 100644 --- a/src/resultsdisassemblypage.cpp +++ b/src/resultsdisassemblypage.cpp @@ -23,7 +23,9 @@ #include #include +#include #include +#include #include "parsers/perf/perfparser.h" #include "resultsutil.h" @@ -42,11 +44,21 @@ #include "models/costdelegate.h" #include "models/disassemblymodel.h" #include "models/hashmodel.h" +#include "models/search.h" #include "models/sourcecodemodel.h" #include "models/topproxy.h" #include "models/treemodel.h" #include "settings.h" +namespace { +template +void connectModel(ModelType* model, ResultFound resultFound, EndReached endReached) +{ + QObject::connect(model, &ModelType::resultFound, model, resultFound); + QObject::connect(model, &ModelType::searchEndReached, model, endReached); +} +} + ResultsDisassemblyPage::ResultsDisassemblyPage(CostContextMenu* costContextMenu, QWidget* parent) : QWidget(parent) , ui(new Ui::ResultsDisassemblyPage) @@ -171,6 +183,76 @@ ResultsDisassemblyPage::ResultsDisassemblyPage(CostContextMenu* costContextMenu, showDisassembly(); }); + ui->searchEndWidget->hide(); + ui->disasmEndReachedWidget->hide(); + + auto setupSearchShortcuts = [this](QPushButton* search, QPushButton* next, QPushButton* prev, QPushButton* close, + QWidget* searchWidget, QLineEdit* edit, QAbstractItemView* view, + KMessageWidget* endReached, auto* model, int additionalRows) { + searchWidget->hide(); + + auto actions = new QActionGroup(view); + + auto findAction = KStandardAction::find( + this, + [searchWidget, edit] { + searchWidget->show(); + edit->setFocus(); + }, + actions); + findAction->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut); + view->addAction(findAction); + + auto searchNext = [this, model, edit, additionalRows] { + int offset = m_currentSearchIndex.isValid() ? m_currentSearchIndex.row() - additionalRows + 1 : 0; + model->find(edit->text(), Direction::Forward, offset); + }; + + auto searchPrev = [this, model, edit, additionalRows] { + int offset = m_currentSearchIndex.isValid() ? m_currentSearchIndex.row() - additionalRows - 1 : 0; + + model->find(edit->text(), Direction::Backward, offset); + }; + + auto findNextAction = KStandardAction::findNext(this, searchNext, actions); + findNextAction->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut); + searchWidget->addAction(findNextAction); + auto findPrevAction = KStandardAction::findPrev(this, searchPrev, actions); + findPrevAction->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut); + searchWidget->addAction(findPrevAction); + + connect(edit, &QLineEdit::returnPressed, findNextAction, &QAction::trigger); + connect(next, &QPushButton::clicked, findNextAction, &QAction::trigger); + connect(prev, &QPushButton::clicked, findPrevAction, &QAction::trigger); + + connect(search, &QPushButton::clicked, findAction, &QAction::trigger); + connect(close, &QPushButton::clicked, this, [searchWidget] { searchWidget->hide(); }); + + KColorScheme colorScheme; + + connectModel( + model, + [this, edit, view, colorScheme](const QModelIndex& index) { + auto palette = edit->palette(); + m_currentSearchIndex = index; + palette.setBrush(QPalette::Text, + index.isValid() ? colorScheme.foreground() + : colorScheme.foreground(KColorScheme::NegativeText)); + edit->setPalette(palette); + view->setCurrentIndex(index); + + if (!index.isValid()) + view->clearSelection(); + }, + [endReached] { endReached->show(); }); + }; + + setupSearchShortcuts(ui->searchButton, ui->nextResult, ui->prevResult, ui->closeButton, ui->searchWidget, + ui->searchEdit, ui->sourceCodeView, ui->searchEndWidget, m_sourceCodeModel, 1); + setupSearchShortcuts(ui->disasmSearchButton, ui->disasmNextButton, ui->disasmPrevButton, ui->disasmCloseButton, + ui->disasmSearchWidget, ui->disasmSearchEdit, ui->assemblyView, ui->disasmEndReachedWidget, + m_disassemblyModel, 0); + #if KF5SyntaxHighlighting_FOUND QStringList schemes; @@ -310,17 +392,18 @@ void ResultsDisassemblyPage::showDisassembly(const DisassemblyOutput& disassembl ResultsUtil::hideEmptyColumns(m_callerCalleeResults.selfCosts, ui->assemblyView, DisassemblyModel::COLUMN_COUNT); - ResultsUtil::hideEmptyColumns(m_callerCalleeResults.selfCosts, ui->sourceCodeView, - SourceCodeModel::COLUMN_COUNT); + ResultsUtil::hideEmptyColumns(m_callerCalleeResults.selfCosts, ui->sourceCodeView, SourceCodeModel::COLUMN_COUNT); ResultsUtil::hideEmptyColumns(m_callerCalleeResults.inclusiveCosts, ui->sourceCodeView, SourceCodeModel::COLUMN_COUNT + m_callerCalleeResults.selfCosts.numTypes()); // hide self cost for tracepoints in assembly view, this is basically always zero - ResultsUtil::hideTracepointColumns(m_callerCalleeResults.selfCosts, ui->assemblyView, DisassemblyModel::COLUMN_COUNT); + ResultsUtil::hideTracepointColumns(m_callerCalleeResults.selfCosts, ui->assemblyView, + DisassemblyModel::COLUMN_COUNT); // hide self cost for tracepoints - only show inclusive times instead here - ResultsUtil::hideTracepointColumns(m_callerCalleeResults.selfCosts, ui->sourceCodeView, SourceCodeModel::COLUMN_COUNT); + ResultsUtil::hideTracepointColumns(m_callerCalleeResults.selfCosts, ui->sourceCodeView, + SourceCodeModel::COLUMN_COUNT); setupAsmViewModel(); } diff --git a/src/resultsdisassemblypage.ui b/src/resultsdisassemblypage.ui index b1813c2a..f5c1c0a9 100644 --- a/src/resultsdisassemblypage.ui +++ b/src/resultsdisassemblypage.ui @@ -113,196 +113,328 @@ - + Qt::Horizontal - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Search reached the end - - 0 + + + + + + true - - 0 + + false - - 0 + + true - - 0 + + + + + + true - - - - true - - - false - - - true - - - - - - - true - - - - 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Search: - - 0 + + + + + + + + + + .. - - 0 + + + + + + + .. - - 0 + + + + + + - - 0 + + + .. - - - - Syntax Highlighting: - - - - - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - 0 + + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Syntax Highlighting: - - 0 + + + + + + true - - 0 + + + + + + + .. - - 0 + + + + + + Qt::Horizontal - - 0 + + + 40 + 20 + - - - - - - - true - - - false - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Syntax Highlighting: - - - - - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Search reached the end + + + + + + + true + + + false + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Search: + + + + + + + + + + + .. + + + + + + + + .. + + + + + + + + + + + .. + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Syntax Highlighting: + + + + + + + true + + + + + + + + + + + .. + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + +