Skip to content

Commit

Permalink
feat: support regex search
Browse files Browse the repository at this point in the history
Since we use QSortFilterProxyModel we get this for free.
This patch adds support for regex search by not escaping the search term
if it is prefix with %.
For the flamegraph there is a custom implementation, which changes the
current QString::contains to a QRegularExpression::match call.

Closes: #666
  • Loading branch information
lievenhey committed Sep 17, 2024
1 parent 36bedef commit a1fe6f0
Show file tree
Hide file tree
Showing 7 changed files with 24 additions and 13 deletions.
21 changes: 12 additions & 9 deletions src/flamegraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,14 +546,15 @@ struct SearchResults
qint64 directCost = 0;
};

SearchResults applySearch(FrameGraphicsItem* item, const QString& searchValue)
SearchResults applySearch(FrameGraphicsItem* item, const QRegularExpression& expression, const QString& pattern)
{
SearchResults result;
if (searchValue.isEmpty()) {
if (expression.pattern().isEmpty()) {
result.matchType = NoSearch;
} else if (item->symbol().symbol.contains(searchValue, Qt::CaseInsensitive)
|| (searchValue == QLatin1String("??") && item->symbol().symbol.isEmpty())
|| item->symbol().binary.contains(searchValue, Qt::CaseInsensitive)) {
} else if (expression.match(item->symbol().symbol).hasMatch()
|| item->symbol().symbol.contains(pattern, Qt::CaseInsensitive)
|| (pattern == QLatin1String("??") && item->symbol().symbol.isEmpty())
|| item->symbol().binary.contains(pattern, Qt::CaseInsensitive)) {
result.directCost += item->cost();
result.matchType = DirectMatch;
}
Expand All @@ -562,7 +563,7 @@ SearchResults applySearch(FrameGraphicsItem* item, const QString& searchValue)
const auto children = item->childItems();
for (auto* child : children) {
auto* childFrame = static_cast<FrameGraphicsItem*>(child);
auto childMatch = applySearch(childFrame, searchValue);
auto childMatch = applySearch(childFrame, expression, pattern);
if (result.matchType != DirectMatch
&& (childMatch.matchType == DirectMatch || childMatch.matchType == ChildMatch)) {
result.matchType = ChildMatch;
Expand Down Expand Up @@ -806,7 +807,8 @@ FlameGraph::FlameGraph(QWidget* parent, Qt::WindowFlags flags)
layout->addWidget(searchInput);

searchInput->setPlaceholderText(i18n("Search..."));
searchInput->setToolTip(i18n("<qt>Search the flame graph for a symbol.</qt>"));
searchInput->setToolTip(tr("<html><head/><body><p>Filter the call graph tree.<br/>Prefix with '%' to turn "
"it into an regex.</p></body></html>"));
searchInput->setClearButtonEnabled(true);
connect(searchInput, &QLineEdit::textChanged, this, &FlameGraph::setSearchValue);
connect(this, &FlameGraph::uiResetRequested, this, [this, searchInput] {
Expand Down Expand Up @@ -1210,9 +1212,10 @@ void FlameGraph::setSearchValue(const QString& value)
return;
}

m_search = value;
m_search = value.startsWith(QLatin1Char('%')) ? value.mid(1) : value;

auto match = applySearch(m_rootItem, value);
const QRegularExpression regex(Util::escapeSearchPatternIfNessessary(value));
auto match = applySearch(m_rootItem, regex, value);

if (value.isEmpty()) {
m_searchResultsLabel->hide();
Expand Down
2 changes: 1 addition & 1 deletion src/resultsbottomuppage.ui
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<item>
<widget class="QLineEdit" name="bottomUpSearch">
<property name="toolTip">
<string>Filter the call graph tree.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Filter the call graph tree.&lt;br/&gt;Prefix with '%' to turn it into an regex.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
Expand Down
2 changes: 1 addition & 1 deletion src/resultscallercalleepage.ui
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<item>
<widget class="QLineEdit" name="callerCalleeFilter">
<property name="toolTip">
<string>Filter the call graph tree.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Filter the call graph tree.&lt;br/&gt;Prefix with '%' to turn it into an regex.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
Expand Down
2 changes: 1 addition & 1 deletion src/resultstopdownpage.ui
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<item>
<widget class="QLineEdit" name="topDownSearch">
<property name="toolTip">
<string>Filter the call graph tree.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Filter the call graph tree.&lt;br/&gt;Prefix with '%' to turn it into an regex.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
Expand Down
2 changes: 1 addition & 1 deletion src/resultsutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void connectFilter(QLineEdit* filter, QSortFilterProxyModel* proxy)
proxy->setFilterCaseSensitivity(Qt::CaseInsensitive);

QObject::connect(timer, &QTimer::timeout, proxy, [filter, proxy]() {
proxy->setFilterRegularExpression(QRegularExpression::escape(filter->text()));
proxy->setFilterRegularExpression(Util::escapeSearchPatternIfNessessary(filter->text()));
});
QObject::connect(filter, &QLineEdit::textChanged, timer, [timer]() { timer->start(300); });
}
Expand Down
5 changes: 5 additions & 0 deletions src/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,8 @@ KParts::ReadOnlyPart* Util::createPart(const QString& pluginName)
return service->createInstance<KParts::ReadOnlyPart>();
#endif
}

QString Util::escapeSearchPatternIfNessessary(const QString& pattern)
{
return pattern.startsWith(QLatin1Char('%')) ? pattern.mid(1) : QRegularExpression::escape(pattern);
}
3 changes: 3 additions & 0 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,7 @@ QString collapseTemplate(const QString& str, int level);
QProcessEnvironment appImageEnvironment();

KParts::ReadOnlyPart* createPart(const QString& pluginName);

// if a pattern is prefixed with % then return the pattern, otherwise do a regex escape
QString escapeSearchPatternIfNessessary(const QString& pattern);
}

0 comments on commit a1fe6f0

Please sign in to comment.