forked from qt-creator/qt-creator
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>
This commit is contained in:
committed by
André Hartmann
parent
2fb54abd03
commit
632f2a7709
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QRegularExpression>
|
||||
|
||||
using namespace Core;
|
||||
|
||||
@@ -201,6 +202,25 @@ bool ILocatorFilter::containsWildcard(const QString &str)
|
||||
return str.contains(QLatin1Char('*')) || str.contains(QLatin1Char('?'));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \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.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
// 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 (index >= 0)
|
||||
filterEntry.highlightInfo = {index, (hasWildcard ? regexp.matchedLength() : entry.length())};
|
||||
|
||||
if (betterMatch)
|
||||
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,
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user