aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndre Hartmann <aha_1980@gmx.de>2017-07-24 20:55:47 +0200
committerAndré Hartmann <aha_1980@gmx.de>2017-09-19 08:24:38 +0000
commit632f2a77098e57eeecdaac39efd32741dfc68e76 (patch)
tree80fa68fca6f958eadd0f095df02a48cb2e438d3f
parent2fb54abd03d71e1e205a1dc6f94769688d1bc2e0 (diff)
Locator: Add camel hump locator filter for C++, QML, and files
* Use the CamelHumpMatcher in the C++, QML, and files filters * Supports matching against UpperCamelCase, lowerCamelCase and snake_case strings * Supports highlighting of matched characters Task-number: QTCREATORBUG-3111 Started-by: David Kaspar <dkaspar@blackberry.com> Change-Id: If6220191432ef965bde3c8dbe4a10d89e222ba6f Reviewed-by: Orgad Shaneh <orgads@gmail.com> Reviewed-by: Eike Ziller <eike.ziller@qt.io>
-rw-r--r--src/libs/utils/camelhumpmatcher.cpp44
-rw-r--r--src/libs/utils/camelhumpmatcher.h10
-rw-r--r--src/libs/utils/highlightingitemdelegate.cpp43
-rw-r--r--src/plugins/coreplugin/locator/basefilefilter.cpp30
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.cpp20
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.h15
-rw-r--r--src/plugins/coreplugin/locator/locatorwidget.cpp3
-rw-r--r--src/plugins/coreplugin/locator/opendocumentsfilter.cpp18
-rw-r--r--src/plugins/cpptools/cppcurrentdocumentfilter.cpp33
-rw-r--r--src/plugins/cpptools/cpplocatorfilter.cpp52
-rw-r--r--src/plugins/cpptools/cpplocatorfilter_test.cpp13
-rw-r--r--src/plugins/qmljstools/qmljsfunctionfilter.cpp35
-rw-r--r--tests/auto/utils/camelhumpmatcher/tst_camelhumpmatcher.cpp61
-rw-r--r--tests/cpplocators/testdata_basic/file1.cpp4
14 files changed, 280 insertions, 101 deletions
diff --git a/src/libs/utils/camelhumpmatcher.cpp b/src/libs/utils/camelhumpmatcher.cpp
index 7c8af9b1292..4ede263d0d4 100644
--- a/src/libs/utils/camelhumpmatcher.cpp
+++ b/src/libs/utils/camelhumpmatcher.cpp
@@ -79,16 +79,19 @@ QRegularExpression CamelHumpMatcher::createCamelHumpRegExp(
else if (c == asterisk)
keyRegExp += ".*";
else
- keyRegExp += QRegularExpression::escape(c);
+ keyRegExp += '(' + QRegularExpression::escape(c) + ')';
} else if (caseSensitivity == CaseSensitivity::CaseInsensitive ||
(caseSensitivity == CaseSensitivity::FirstLetterCaseSensitive && !first)) {
keyRegExp += "(?:";
keyRegExp += first ? uppercaseWordFirst : uppercaseWordContinuation;
- keyRegExp += QRegularExpression::escape(c.toUpper());
- keyRegExp += '|';
- keyRegExp += first ? lowercaseWordFirst : lowercaseWordContinuation;
- keyRegExp += QRegularExpression::escape(c.toLower());
+ keyRegExp += '(' + QRegularExpression::escape(c.toUpper());
+ if (first) {
+ keyRegExp += '|' + lowercaseWordFirst + QRegularExpression::escape(c.toLower()) + ')';
+ } else {
+ keyRegExp += ")|" + lowercaseWordContinuation;
+ keyRegExp += '(' + QRegularExpression::escape(c.toLower()) + ')';
+ }
keyRegExp += ')';
} else {
if (!first) {
@@ -104,3 +107,34 @@ QRegularExpression CamelHumpMatcher::createCamelHumpRegExp(
}
return QRegularExpression(keyRegExp);
}
+
+/*!
+ * \brief Returns a list of matched character positions and their matched lengths for the
+ * given regular expression \a match.
+ *
+ * The list is minimized by combining adjacent highlighting positions to a single position.
+ */
+CamelHumpMatcher::HighlightingPositions CamelHumpMatcher::highlightingPositions(
+ const QRegularExpressionMatch &match)
+{
+ HighlightingPositions result;
+
+ for (int i = 1, size = match.capturedTexts().size(); i < size; ++i) {
+ // skip unused positions, they can appear because upper- and lowercase
+ // checks for one character are done using two capture groups
+ if (match.capturedStart(i) < 0)
+ continue;
+
+ // check for possible highlighting continuation to keep the list minimal
+ if (!result.starts.isEmpty()
+ && (result.starts.last() + result.lengths.last() == match.capturedStart(i))) {
+ result.lengths.last() += match.capturedLength(i);
+ } else {
+ // no continuation, append as different chunk
+ result.starts.append(match.capturedStart(i));
+ result.lengths.append(match.capturedLength(i));
+ }
+ }
+
+ return result;
+}
diff --git a/src/libs/utils/camelhumpmatcher.h b/src/libs/utils/camelhumpmatcher.h
index 8364678ab47..8021c4ddf68 100644
--- a/src/libs/utils/camelhumpmatcher.h
+++ b/src/libs/utils/camelhumpmatcher.h
@@ -29,8 +29,11 @@
#include "utils_global.h"
+#include <QVector>
+
QT_BEGIN_NAMESPACE
class QRegularExpression;
+class QRegularExpressionMatch;
class QString;
QT_END_NAMESPACE
@@ -43,6 +46,13 @@ public:
FirstLetterCaseSensitive
};
+ class HighlightingPositions {
+ public:
+ QVector<int> starts;
+ QVector<int> lengths;
+ };
+
static QRegularExpression createCamelHumpRegExp(const QString &pattern,
CaseSensitivity caseSensitivity = CaseSensitivity::CaseInsensitive);
+ static HighlightingPositions highlightingPositions(const QRegularExpressionMatch &match);
};
diff --git a/src/libs/utils/highlightingitemdelegate.cpp b/src/libs/utils/highlightingitemdelegate.cpp
index fe2525122c8..c9bb43eb948 100644
--- a/src/libs/utils/highlightingitemdelegate.cpp
+++ b/src/libs/utils/highlightingitemdelegate.cpp
@@ -155,28 +155,37 @@ void HighlightingItemDelegate::drawText(QPainter *painter,
if (index.model()->hasChildren(index))
text += " (" + QString::number(index.model()->rowCount(index)) + ')';
- int searchTermStart = index.model()->data(index, int(HighlightingItemRole::StartColumn)).toInt();
- int searchTermLength = index.model()->data(index, int(HighlightingItemRole::Length)).toInt();
- if (searchTermStart < 0 || searchTermStart >= text.length() || searchTermLength < 1) {
+ QVector<int> searchTermStarts =
+ index.model()->data(index, int(HighlightingItemRole::StartColumn)).value<QVector<int>>();
+ QVector<int> searchTermLengths =
+ index.model()->data(index, int(HighlightingItemRole::Length)).value<QVector<int>>();
+
+ if (searchTermStarts.isEmpty()) {
drawDisplay(painter, option, rect, text.replace('\t', m_tabString), {});
return;
}
// replace tabs with searchTerm bookkeeping
- int searchTermEnd = searchTermStart + searchTermLength;
const int tabDiff = m_tabString.size() - 1;
for (int i = 0; i < text.length(); i++) {
- if (text.at(i) == '\t') {
- text.replace(i, 1, m_tabString);
- if (i < searchTermStart) {
- searchTermStart += tabDiff;
- searchTermEnd += tabDiff;
- } else if (i < searchTermEnd) {
- searchTermEnd += tabDiff;
- searchTermLength += tabDiff;
- }
- i += tabDiff;
+ if (text.at(i) != '\t')
+ continue;
+
+ text.replace(i, 1, m_tabString);
+
+ // adjust highlighting length if tab is highlighted
+ for (int j = 0; j < searchTermStarts.size(); ++j) {
+ if (i >= searchTermStarts.at(j) && i < searchTermStarts.at(j) + searchTermLengths.at(j))
+ searchTermLengths[j] += tabDiff;
}
+
+ // adjust all following highlighting starts
+ for (int j = 0; j < searchTermStarts.size(); ++j) {
+ if (searchTermStarts.at(j) > i)
+ searchTermStarts[j] += tabDiff;
+ }
+
+ i += tabDiff;
}
const QColor highlightForeground =
@@ -187,7 +196,11 @@ void HighlightingItemDelegate::drawText(QPainter *painter,
highlightFormat.setForeground(highlightForeground);
highlightFormat.setBackground(highlightBackground);
- drawDisplay(painter, option, rect, text, {{searchTermStart, searchTermLength, highlightFormat}});
+ QVector<QTextLayout::FormatRange> formats;
+ for (int i = 0, size = searchTermStarts.size(); i < size; ++i)
+ formats.append({searchTermStarts.at(i), searchTermLengths.at(i), highlightFormat});
+
+ drawDisplay(painter, option, rect, text, formats);
}
// copied from QItemDelegate for drawDisplay
diff --git a/src/plugins/coreplugin/locator/basefilefilter.cpp b/src/plugins/coreplugin/locator/basefilefilter.cpp
index 4d7cb124f38..86fe5078f85 100644
--- a/src/plugins/coreplugin/locator/basefilefilter.cpp
+++ b/src/plugins/coreplugin/locator/basefilefilter.cpp
@@ -26,12 +26,12 @@
#include "basefilefilter.h"
#include <coreplugin/editormanager/editormanager.h>
+#include <utils/camelhumpmatcher.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <QDir>
-#include <QRegExp>
-#include <QStringMatcher>
+#include <QRegularExpression>
#include <QTimer>
using namespace Core;
@@ -100,16 +100,15 @@ QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<LocatorFil
QList<LocatorFilterEntry> goodEntries;
const QString entry = QDir::fromNativeSeparators(origEntry);
const EditorManager::FilePathInfo fp = EditorManager::splitLineAndColumnNumber(entry);
- const Qt::CaseSensitivity cs = caseSensitivity(fp.filePath);
- QStringMatcher matcher(fp.filePath, cs);
- QRegExp regexp(fp.filePath, cs, QRegExp::Wildcard);
+ const QRegularExpression regexp = containsWildcard(entry)
+ ? createWildcardRegExp(entry) : CamelHumpMatcher::createCamelHumpRegExp(entry);
+
if (!regexp.isValid()) {
d->m_current.clear(); // free memory
return betterEntries;
}
const QChar pathSeparator(QLatin1Char('/'));
const bool hasPathSeparator = fp.filePath.contains(pathSeparator);
- const bool hasWildcard = containsWildcard(fp.filePath);
const bool containsPreviousEntry = !d->m_current.previousEntry.isEmpty()
&& fp.filePath.contains(d->m_current.previousEntry);
const bool pathSeparatorAdded = !d->m_current.previousEntry.contains(pathSeparator)
@@ -136,27 +135,24 @@ QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<LocatorFil
QString path = d->m_current.iterator->filePath();
QString name = d->m_current.iterator->fileName();
QString matchText = hasPathSeparator ? path : name;
- int index = hasWildcard ? regexp.indexIn(matchText) : matcher.indexIn(matchText);
+ QRegularExpressionMatch match = regexp.match(matchText);
- if (index >= 0) {
+ if (match.hasMatch()) {
QFileInfo fi(path);
LocatorFilterEntry filterEntry(this, fi.fileName(), QString(path + fp.postfix));
filterEntry.fileName = path;
filterEntry.extraInfo = FileUtils::shortNativePath(FileName(fi));
LocatorFilterEntry::HighlightInfo::DataType hDataType = LocatorFilterEntry::HighlightInfo::DisplayName;
- int length = hasWildcard ? regexp.matchedLength() : fp.filePath.length();
- const bool betterMatch = index == 0;
+ const bool betterMatch = match.capturedStart() == 0;
if (hasPathSeparator) {
- const int indexCandidate = index + filterEntry.extraInfo.length() - path.length();
- const int cutOff = indexCandidate < 0 ? -indexCandidate : 0;
- index = qMax(indexCandidate, 0);
- length = qMax(length - cutOff, 1);
+ match = regexp.match(filterEntry.extraInfo);
hDataType = LocatorFilterEntry::HighlightInfo::ExtraInfo;
}
-
- if (index >= 0)
- filterEntry.highlightInfo = LocatorFilterEntry::HighlightInfo(index, length, hDataType);
+ const CamelHumpMatcher::HighlightingPositions positions =
+ CamelHumpMatcher::highlightingPositions(match);
+ filterEntry.highlightInfo =
+ LocatorFilterEntry::HighlightInfo(positions.starts, positions.lengths, hDataType);
if (betterMatch)
betterEntries.append(filterEntry);
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.cpp b/src/plugins/coreplugin/locator/ilocatorfilter.cpp
index 3b5736f95ba..3a0d897a63f 100644
--- a/src/plugins/coreplugin/locator/ilocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.cpp
@@ -34,6 +34,7 @@
#include <QDialogButtonBox>
#include <QLabel>
#include <QLineEdit>
+#include <QRegularExpression>
using namespace Core;
@@ -202,6 +203,25 @@ bool ILocatorFilter::containsWildcard(const QString &str)
}
/*!
+ * \brief Returns a simple regular expression to search for \a text.
+ *
+ * \a text may contain the simple '?' and '*' wildcards known from the shell.
+ * '?' matches exactly one character, '*' matches a number of characters
+ * (including none).
+ *
+ * The regular expression contains capture groups to allow highlighting
+ * matched characters after a match.
+ */
+QRegularExpression ILocatorFilter::createWildcardRegExp(const QString &text)
+{
+ QString pattern = '(' + text + ')';
+ pattern.replace('?', ").(");
+ pattern.replace('*', ").*(");
+ pattern.remove("()");
+ return QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption);
+}
+
+/*!
Specifies a title for configuration dialogs.
*/
QString ILocatorFilter::msgConfigureDialogTitle()
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.h b/src/plugins/coreplugin/locator/ilocatorfilter.h
index e649a1c959d..e48464f86fc 100644
--- a/src/plugins/coreplugin/locator/ilocatorfilter.h
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.h
@@ -46,13 +46,19 @@ struct LocatorFilterEntry
};
HighlightInfo(int startIndex, int length, DataType type = DataType::DisplayName)
- : startIndex(startIndex)
- , length(length)
+ : starts{startIndex}
+ , lengths{length}
, dataType(type)
{}
- int startIndex;
- int length;
+ HighlightInfo(QVector<int> startIndex, QVector<int> length, DataType type = DataType::DisplayName)
+ : starts(startIndex)
+ , lengths(length)
+ , dataType(type)
+ {}
+
+ QVector<int> starts;
+ QVector<int> lengths;
DataType dataType;
};
@@ -138,6 +144,7 @@ public:
static Qt::CaseSensitivity caseSensitivity(const QString &str);
static bool containsWildcard(const QString &str);
+ static QRegularExpression createWildcardRegExp(const QString &text);
static QString msgConfigureDialogTitle();
static QString msgPrefixLabel();
diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp
index dcacc1a4f1a..3ba6b7f3d14 100644
--- a/src/plugins/coreplugin/locator/locatorwidget.cpp
+++ b/src/plugins/coreplugin/locator/locatorwidget.cpp
@@ -217,7 +217,8 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
: ExtraInfoColumn;
if (highlightColumn == index.column()) {
const bool startIndexRole = role == int(HighlightingItemRole::StartColumn);
- return startIndexRole ? entry.highlightInfo.startIndex : entry.highlightInfo.length;
+ return startIndexRole ? QVariant::fromValue(entry.highlightInfo.starts)
+ : QVariant::fromValue(entry.highlightInfo.lengths);
}
break;
}
diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
index 8975aeb6e0b..b9b1779529e 100644
--- a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
+++ b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
@@ -27,12 +27,13 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
+#include <utils/camelhumpmatcher.h>
#include <utils/fileutils.h>
#include <QAbstractItemModel>
#include <QFileInfo>
#include <QMutexLocker>
-#include <QRegExp>
+#include <QRegularExpression>
using namespace Core;
using namespace Core::Internal;
@@ -60,7 +61,9 @@ QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Locat
QList<LocatorFilterEntry> goodEntries;
QList<LocatorFilterEntry> betterEntries;
const EditorManager::FilePathInfo fp = EditorManager::splitLineAndColumnNumber(entry);
- QRegExp regexp(fp.filePath, caseSensitivity(fp.filePath), QRegExp::Wildcard);
+ const QRegularExpression regexp = containsWildcard(entry)
+ ? createWildcardRegExp(entry) : CamelHumpMatcher::createCamelHumpRegExp(entry);
+
if (!regexp.isValid())
return goodEntries;
@@ -71,13 +74,16 @@ QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Locat
if (fileName.isEmpty())
continue;
QString displayName = editorEntry.displayName;
- const int index = regexp.indexIn(displayName);
- if (index >= 0) {
+ const QRegularExpressionMatch match = regexp.match(displayName);
+ if (match.hasMatch()) {
+ const CamelHumpMatcher::HighlightingPositions positions =
+ CamelHumpMatcher::highlightingPositions(match);
LocatorFilterEntry filterEntry(this, displayName, QString(fileName + fp.postfix));
filterEntry.extraInfo = FileUtils::shortNativePath(FileName::fromString(fileName));
filterEntry.fileName = fileName;
- filterEntry.highlightInfo = {index, regexp.matchedLength()};
- if (index == 0)
+ filterEntry.highlightInfo.starts = positions.starts;
+ filterEntry.highlightInfo.lengths = positions.lengths;
+ if (match.capturedStart() == 0)
betterEntries.append(filterEntry);
else
goodEntries.append(filterEntry);
diff --git a/src/plugins/cpptools/cppcurrentdocumentfilter.cpp b/src/plugins/cpptools/cppcurrentdocumentfilter.cpp
index bc0584a0a56..6f528a3f4f5 100644
--- a/src/plugins/cpptools/cppcurrentdocumentfilter.cpp
+++ b/src/plugins/cpptools/cppcurrentdocumentfilter.cpp
@@ -30,9 +30,9 @@
#include <coreplugin/idocument.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
+#include <utils/camelhumpmatcher.h>
-#include <QRegExp>
-#include <QStringMatcher>
+#include <QRegularExpression>
using namespace CppTools::Internal;
using namespace CPlusPlus;
@@ -66,12 +66,12 @@ QList<Core::LocatorFilterEntry> CppCurrentDocumentFilter::matchesFor(
{
QList<Core::LocatorFilterEntry> goodEntries;
QList<Core::LocatorFilterEntry> betterEntries;
- const Qt::CaseSensitivity cs = caseSensitivity(entry);
- QStringMatcher matcher(entry, cs);
- QRegExp regexp(entry, cs, QRegExp::Wildcard);
+
+ const QRegularExpression regexp = containsWildcard(entry)
+ ? createWildcardRegExp(entry) : CamelHumpMatcher::createCamelHumpRegExp(entry);
+
if (!regexp.isValid())
return goodEntries;
- bool hasWildcard = containsWildcard(entry);
foreach (IndexItem::Ptr info, itemsOfCurrentDocument()) {
if (future.isCanceled())
@@ -83,29 +83,30 @@ QList<Core::LocatorFilterEntry> CppCurrentDocumentFilter::matchesFor(
else if (info->type() == IndexItem::Function)
matchString += info->symbolType();
- int index = hasWildcard ? regexp.indexIn(matchString) : matcher.indexIn(matchString);
- if (index >= 0) {
- const bool betterMatch = index == 0;
+ QRegularExpressionMatch match = regexp.match(matchString);
+ if (match.hasMatch()) {
+ const bool betterMatch = match.capturedStart() == 0;
QVariant id = qVariantFromValue(info);
QString name = matchString;
QString extraInfo = info->symbolScope();
if (info->type() == IndexItem::Function) {
if (info->unqualifiedNameAndScope(matchString, &name, &extraInfo)) {
name += info->symbolType();
- index = hasWildcard ? regexp.indexIn(name) : matcher.indexIn(name);
+ match = regexp.match(name);
}
}
Core::LocatorFilterEntry filterEntry(this, name, id, info->icon());
filterEntry.extraInfo = extraInfo;
- if (index < 0) {
- index = hasWildcard ? regexp.indexIn(extraInfo) : matcher.indexIn(extraInfo);
+ if (!match.hasMatch()) {
+ match = regexp.match(extraInfo);
filterEntry.highlightInfo.dataType = Core::LocatorFilterEntry::HighlightInfo::ExtraInfo;
}
- if (index >= 0) {
- filterEntry.highlightInfo.startIndex = index;
- filterEntry.highlightInfo.length = hasWildcard ? regexp.matchedLength() : entry.length();
- }
+ const CamelHumpMatcher::HighlightingPositions positions =
+ CamelHumpMatcher::highlightingPositions(match);
+ filterEntry.highlightInfo.starts = positions.starts;
+ filterEntry.highlightInfo.lengths = positions.lengths;
+
if (betterMatch)
betterEntries.append(filterEntry);
else
diff --git a/src/plugins/cpptools/cpplocatorfilter.cpp b/src/plugins/cpptools/cpplocatorfilter.cpp
index f8bf53dd325..86028fbe624 100644
--- a/src/plugins/cpptools/cpplocatorfilter.cpp
+++ b/src/plugins/cpptools/cpplocatorfilter.cpp
@@ -28,9 +28,9 @@
#include <coreplugin/editormanager/editormanager.h>
#include <utils/algorithm.h>
+#include <utils/camelhumpmatcher.h>
-#include <QRegExp>
-#include <QStringMatcher>
+#include <QRegularExpression>
#include <algorithm>
@@ -72,34 +72,37 @@ QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
{
QList<Core::LocatorFilterEntry> goodEntries;
QList<Core::LocatorFilterEntry> betterEntries;
- const Qt::CaseSensitivity cs = caseSensitivity(entry);
- QStringMatcher matcher(entry, cs);
- QRegExp regexp(entry, cs, QRegExp::Wildcard);
- if (!regexp.isValid())
- return goodEntries;
- bool hasWildcard = containsWildcard(entry);
+ QList<Core::LocatorFilterEntry> bestEntries;
+ const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
bool hasColonColon = entry.contains(QLatin1String("::"));
const IndexItem::ItemType wanted = matchTypes();
+ const QRegularExpression regexp = containsWildcard(entry)
+ ? createWildcardRegExp(entry) : CamelHumpMatcher::createCamelHumpRegExp(entry);
+
+ if (!regexp.isValid())
+ return goodEntries;
m_data->filterAllFiles([&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult {
if (future.isCanceled())
return IndexItem::Break;
if (info->type() & wanted) {
- const QString matchString = hasColonColon ? info->scopedSymbolName() : info->symbolName();
- int index = hasWildcard ? regexp.indexIn(matchString) : matcher.indexIn(matchString);
- if (index >= 0) {
- const bool betterMatch = index == 0;
+ QString matchString = hasColonColon ? info->scopedSymbolName() : info->symbolName();
+ QRegularExpressionMatch match = regexp.match(matchString);
+ if (match.hasMatch()) {
Core::LocatorFilterEntry filterEntry = filterEntryFromIndexItem(info);
- if (matchString != filterEntry.displayName) {
- index = hasWildcard ? regexp.indexIn(filterEntry.displayName)
- : matcher.indexIn(filterEntry.displayName);
- }
-
- if (index >= 0)
- filterEntry.highlightInfo = {index, (hasWildcard ? regexp.matchedLength() : entry.length())};
-
- if (betterMatch)
+ // Highlight the matched characters, therefore it may be necessary
+ // to update the match if the displayName is different from matchString
+ if (matchString != filterEntry.displayName)
+ match = regexp.match(filterEntry.displayName);
+ const CamelHumpMatcher::HighlightingPositions positions =
+ CamelHumpMatcher::highlightingPositions(match);
+ filterEntry.highlightInfo.starts = positions.starts;
+ filterEntry.highlightInfo.lengths = positions.lengths;
+
+ if (matchString.startsWith(entry, caseSensitivityForPrefix))
+ bestEntries.append(filterEntry);
+ else if (matchString.contains(entry, caseSensitivityForPrefix))
betterEntries.append(filterEntry);
else
goodEntries.append(filterEntry);
@@ -116,9 +119,12 @@ QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
Utils::sort(goodEntries, Core::LocatorFilterEntry::compareLexigraphically);
if (betterEntries.size() < 1000)
Utils::sort(betterEntries, Core::LocatorFilterEntry::compareLexigraphically);
+ if (bestEntries.size() < 1000)
+ Utils::sort(bestEntries, Core::LocatorFilterEntry::compareLexigraphically);
- betterEntries += goodEntries;
- return betterEntries;
+ bestEntries += betterEntries;
+ bestEntries += goodEntries;
+ return bestEntries;
}
void CppLocatorFilter::accept(Core::LocatorFilterEntry selection,
diff --git a/src/plugins/cpptools/cpplocatorfilter_test.cpp b/src/plugins/cpptools/cpplocatorfilter_test.cpp
index 27258855d80..501a396eb13 100644
--- a/src/plugins/cpptools/cpplocatorfilter_test.cpp
+++ b/src/plugins/cpptools/cpplocatorfilter_test.cpp
@@ -190,6 +190,16 @@ void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter_data()
<< ResultData(_("myFunction(bool, int)"), _("<anonymous namespace> (file1.cpp)"))
);
+ QTest::newRow("CppFunctionsFilter-Sorting")
+ << testFile
+ << cppFunctionsFilter
+ << _("pos")
+ << (QList<ResultData>()
+ << ResultData(_("positiveNumber()"), testFileShort)
+ << ResultData(_("getPosition()"), testFileShort)
+ << ResultData(_("pointOfService()"), testFileShort)
+ );
+
QTest::newRow("CppFunctionsFilter-WithNamespacePrefix")
<< testFile
<< cppFunctionsFilter
@@ -280,6 +290,9 @@ void CppToolsPlugin::test_cpplocatorfilters_CppCurrentDocumentFilter()
QList<ResultData> expectedResults = QList<ResultData>()
<< ResultData(_("int myVariable"), _(""))
<< ResultData(_("myFunction(bool, int)"), _(""))
+ << ResultData(_("pointOfService()"), _(""))
+ << ResultData(_("getPosition()"), _(""))
+ << ResultData(_("positiveNumber()"), _(""))
<< ResultData(_("MyEnum"), _(""))
<< ResultData(_("int V1"), _("MyEnum"))
<< ResultData(_("int V2"), _("MyEnum"))
diff --git a/src/plugins/qmljstools/qmljsfunctionfilter.cpp b/src/plugins/qmljstools/qmljsfunctionfilter.cpp
index 7c00e6c9f02..02081f7e906 100644
--- a/src/plugins/qmljstools/qmljsfunctionfilter.cpp
+++ b/src/plugins/qmljstools/qmljsfunctionfilter.cpp
@@ -28,9 +28,9 @@
#include <coreplugin/editormanager/editormanager.h>
#include <utils/algorithm.h>
+#include <utils/camelhumpmatcher.h>
-#include <QRegExp>
-#include <QStringMatcher>
+#include <QRegularExpression>
using namespace QmlJSTools::Internal;
@@ -59,12 +59,13 @@ QList<Core::LocatorFilterEntry> FunctionFilter::matchesFor(
{
QList<Core::LocatorFilterEntry> goodEntries;
QList<Core::LocatorFilterEntry> betterEntries;
- const Qt::CaseSensitivity cs = caseSensitivity(entry);
- QStringMatcher matcher(entry, cs);
- QRegExp regexp(entry, cs, QRegExp::Wildcard);
+ QList<Core::LocatorFilterEntry> bestEntries;
+ const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
+ const QRegularExpression regexp = containsWildcard(entry)
+ ? createWildcardRegExp(entry) : CamelHumpMatcher::createCamelHumpRegExp(entry);
+
if (!regexp.isValid())
return goodEntries;
- bool hasWildcard = containsWildcard(entry);
QHashIterator<QString, QList<LocatorData::Entry> > it(m_data->entries());
while (it.hasNext()) {
@@ -78,16 +79,19 @@ QList<Core::LocatorFilterEntry> FunctionFilter::matchesFor(
if (info.type != LocatorData::Function)
continue;
- const int index = hasWildcard ? regexp.indexIn(info.symbolName)
- : matcher.indexIn(info.symbolName);
- if (index >= 0) {
+ const QRegularExpressionMatch match = regexp.match(info.symbolName);
+ if (match.hasMatch()) {
QVariant id = qVariantFromValue(info);
Core::LocatorFilterEntry filterEntry(this, info.displayName, id/*, info.icon*/);
+ const CamelHumpMatcher::HighlightingPositions positions =
+ CamelHumpMatcher::highlightingPositions(match);
filterEntry.extraInfo = info.extraInfo;
- const int length = hasWildcard ? regexp.matchedLength() : entry.length();
- filterEntry.highlightInfo = {index, length};
+ filterEntry.highlightInfo.starts = positions.starts;
+ filterEntry.highlightInfo.lengths = positions.lengths;
- if (index == 0)
+ if (filterEntry.displayName.startsWith(entry, caseSensitivityForPrefix))
+ bestEntries.append(filterEntry);
+ else if (filterEntry.displayName.contains(entry, caseSensitivityForPrefix))
betterEntries.append(filterEntry);
else
goodEntries.append(filterEntry);
@@ -99,9 +103,12 @@ QList<Core::LocatorFilterEntry> FunctionFilter::matchesFor(
Utils::sort(goodEntries, Core::LocatorFilterEntry::compareLexigraphically);
if (betterEntries.size() < 1000)
Utils::sort(betterEntries, Core::LocatorFilterEntry::compareLexigraphically);
+ if (bestEntries.size() < 1000)
+ Utils::sort(bestEntries, Core::LocatorFilterEntry::compareLexigraphically);
- betterEntries += goodEntries;
- return betterEntries;
+ bestEntries += betterEntries;
+ bestEntries += goodEntries;
+ return bestEntries;
}
void FunctionFilter::accept(Core::LocatorFilterEntry selection,
diff --git a/tests/auto/utils/camelhumpmatcher/tst_camelhumpmatcher.cpp b/tests/auto/utils/camelhumpmatcher/tst_camelhumpmatcher.cpp
index b42a46cba71..dea71dbf235 100644
--- a/tests/auto/utils/camelhumpmatcher/tst_camelhumpmatcher.cpp
+++ b/tests/auto/utils/camelhumpmatcher/tst_camelhumpmatcher.cpp
@@ -35,6 +35,8 @@ class tst_CamelHumpMatcher : public QObject
private slots:
void camelHumpMatcher();
void camelHumpMatcher_data();
+ void highlighting();
+ void highlighting_data();
};
void tst_CamelHumpMatcher::camelHumpMatcher()
@@ -75,5 +77,64 @@ void tst_CamelHumpMatcher::camelHumpMatcher_data()
QTest::newRow("middle-continued") << "cahu" << "LongCamelHump" << 4;
}
+typedef QVector<int> MatchStart;
+typedef QVector<int> MatchLength;
+
+void tst_CamelHumpMatcher::highlighting()
+{
+ QFETCH(QString, pattern);
+ QFETCH(QString, candidate);
+ QFETCH(MatchStart, matchStart);
+ QFETCH(MatchLength, matchLength);
+
+ const QRegularExpression regExp = CamelHumpMatcher::createCamelHumpRegExp(pattern);
+ const QRegularExpressionMatch match = regExp.match(candidate);
+ const CamelHumpMatcher::HighlightingPositions positions =
+ CamelHumpMatcher::highlightingPositions(match);
+
+ QCOMPARE(positions.starts.size(), matchStart.size());
+ for (int i = 0; i < positions.starts.size(); ++i) {
+ QCOMPARE(positions.starts.at(i), matchStart.at(i));
+ QCOMPARE(positions.lengths.at(i), matchLength.at(i));
+ }
+}
+
+void tst_CamelHumpMatcher::highlighting_data()
+{
+ QTest::addColumn<QString>("pattern");
+ QTest::addColumn<QString>("candidate");
+ QTest::addColumn<MatchStart>("matchStart");
+ QTest::addColumn<MatchLength>("matchLength");
+
+ QTest::newRow("prefix-snake") << "very" << "very_long_camel_hump"
+ << MatchStart{0} << MatchLength{4};
+ QTest::newRow("middle-snake") << "long" << "very_long_camel_hump"
+ << MatchStart{5} << MatchLength{4};
+ QTest::newRow("suffix-snake") << "hump" << "very_long_camel_hump"
+ << MatchStart{16} << MatchLength{4};
+ QTest::newRow("prefix-camel") << "very" << "VeryLongCamelHump"
+ << MatchStart{0} << MatchLength{4};
+ QTest::newRow("middle-camel") << "Long" << "VeryLongCamelHump"
+ << MatchStart{4} << MatchLength{4};
+ QTest::newRow("suffix-camel") << "Hump" << "VeryLongCamelHump"
+ << MatchStart{13} << MatchLength{4};
+ QTest::newRow("humps-camel") << "vlch" << "VeryLongCamelHump"
+ << MatchStart{0, 4, 8, 13} << MatchLength{1, 1, 1, 1};
+ QTest::newRow("humps-camel-lower") << "vlch" << "veryLongCamelHump"
+ << MatchStart{0, 4, 8, 13} << MatchLength{1, 1, 1, 1};
+ QTest::newRow("humps-snake") << "vlch" << "very_long_camel_hump"
+ << MatchStart{0, 5, 10, 16} << MatchLength{1, 1, 1, 1};
+ QTest::newRow("humps-middle") << "lc" << "VeryLongCamelHump"
+ << MatchStart{4, 8} << MatchLength{1, 1};
+ QTest::newRow("humps-last") << "h" << "VeryLongCamelHump"
+ << MatchStart{13} << MatchLength{1};
+ QTest::newRow("humps-continued") << "LoCa" << "VeryLongCamelHump"
+ << MatchStart{4, 8} << MatchLength{2, 2};
+ QTest::newRow("wildcard-asterisk") << "Lo*Hu" << "VeryLongCamelHump"
+ << MatchStart{4, 13} << MatchLength{2, 2};
+ QTest::newRow("wildcard-question") << "Lo?g" << "VeryLongCamelHump"
+ << MatchStart{4, 7} << MatchLength{2, 1};
+}
+
QTEST_APPLESS_MAIN(tst_CamelHumpMatcher)
#include "tst_camelhumpmatcher.moc"
diff --git a/tests/cpplocators/testdata_basic/file1.cpp b/tests/cpplocators/testdata_basic/file1.cpp
index 98a53752377..5c1e3caff32 100644
--- a/tests/cpplocators/testdata_basic/file1.cpp
+++ b/tests/cpplocators/testdata_basic/file1.cpp
@@ -12,6 +12,10 @@ int myVariable;
int myFunction(bool yesno, int number) {}
+void pointOfService() {}
+int getPosition() { return 0; }
+int positiveNumber() { return 2; }
+
enum MyEnum { V1, V2 };
class MyClass