Locator: Add highlighting of the search text

Change-Id: Ia166e9667076e46770a754b626ceb28080139e79
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Serhii Moroz
2016-08-02 12:04:40 +03:00
parent 4f66adb9a2
commit 6ecb1c4018
22 changed files with 267 additions and 153 deletions

View File

@@ -116,6 +116,7 @@ SplitterColor=ff313131
TextColorDisabled=textDisabled TextColorDisabled=textDisabled
TextColorError=ffff4040 TextColorError=ffff4040
TextColorHighlight=ffff0000 TextColorHighlight=ffff0000
TextColorHighlightBackground=555500
TextColorLink=textColorLink TextColorLink=textColorLink
TextColorLinkVisited=textColorLinkVisited TextColorLinkVisited=textColorLinkVisited
TextColorNormal=text TextColorNormal=text

View File

@@ -108,6 +108,7 @@ SplitterColor=ff151515
TextColorDisabled=ff000000 TextColorDisabled=ff000000
TextColorError=ffff0000 TextColorError=ffff0000
TextColorHighlight=ffa0a0a4 TextColorHighlight=ffa0a0a4
TextColorHighlightBackground=ffef0b
TextColorLink=ff0057ae TextColorLink=ff0057ae
TextColorLinkVisited=ff644a9b TextColorLinkVisited=ff644a9b
TextColorNormal=ff000000 TextColorNormal=ff000000

View File

@@ -121,6 +121,7 @@ SplitterColor=splitter
TextColorDisabled=textDisabled TextColorDisabled=textDisabled
TextColorError=ffff4040 TextColorError=ffff4040
TextColorHighlight=ffff0000 TextColorHighlight=ffff0000
TextColorHighlightBackground=8a7f2c
TextColorLink=textColorLink TextColorLink=textColorLink
TextColorLinkVisited=textColorLinkVisited TextColorLinkVisited=textColorLinkVisited
TextColorNormal=text TextColorNormal=text

View File

@@ -119,6 +119,7 @@ SplitterColor=splitter
TextColorDisabled=textDisabled TextColorDisabled=textDisabled
TextColorError=ffff4040 TextColorError=ffff4040
TextColorHighlight=ffff0000 TextColorHighlight=ffff0000
TextColorHighlightBackground=ffef0b
TextColorLink=ff007af4 TextColorLink=ff007af4
TextColorLinkVisited=ffa57aff TextColorLinkVisited=ffa57aff
TextColorNormal=text TextColorNormal=text

View File

@@ -117,6 +117,7 @@ SplitterColor=splitter
TextColorDisabled=textDisabled TextColorDisabled=textDisabled
TextColorError=ffff4040 TextColorError=ffff4040
TextColorHighlight=ffff0000 TextColorHighlight=ffff0000
TextColorHighlightBackground=ffef0b
TextColorLink=ff007af4 TextColorLink=ff007af4
TextColorLinkVisited=ffa57aff TextColorLinkVisited=ffa57aff
TextColorNormal=text TextColorNormal=text

View File

@@ -116,6 +116,7 @@ public:
TextColorDisabled, TextColorDisabled,
TextColorError, TextColorError,
TextColorHighlight, TextColorHighlight,
TextColorHighlightBackground,
TextColorLink, TextColorLink,
TextColorLinkVisited, TextColorLinkVisited,
TextColorNormal, TextColorNormal,

View File

@@ -65,10 +65,12 @@ void CMakeLocatorFilter::prepareSearch(const QString &entry)
if (!cmakeProject) if (!cmakeProject)
continue; continue;
foreach (const QString &title, cmakeProject->buildTargetTitles()) { foreach (const QString &title, cmakeProject->buildTargetTitles()) {
if (title.contains(entry)) { const int index = title.indexOf(entry);
Core::LocatorFilterEntry entry(this, title, cmakeProject->projectFilePath().toString()); if (index >= 0) {
entry.extraInfo = FileUtils::shortNativePath(cmakeProject->projectFilePath()); Core::LocatorFilterEntry filterEntry(this, title, cmakeProject->projectFilePath().toString());
m_result.append(entry); filterEntry.extraInfo = FileUtils::shortNativePath(cmakeProject->projectFilePath());
filterEntry.highlightInfo = {index, entry.length()};
m_result.append(filterEntry);
} }
} }
} }

View File

@@ -98,18 +98,18 @@ QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<LocatorFil
{ {
QList<LocatorFilterEntry> betterEntries; QList<LocatorFilterEntry> betterEntries;
QList<LocatorFilterEntry> goodEntries; QList<LocatorFilterEntry> goodEntries;
const QString trimmed = trimWildcards(QDir::fromNativeSeparators(origEntry)); const QString entry = QDir::fromNativeSeparators(origEntry);
const EditorManager::FilePathInfo fp = EditorManager::splitLineAndColumnNumber(trimmed); const EditorManager::FilePathInfo fp = EditorManager::splitLineAndColumnNumber(entry);
QStringMatcher matcher(fp.filePath, Qt::CaseInsensitive); const Qt::CaseSensitivity cs = caseSensitivity(fp.filePath);
const QChar asterisk = QLatin1Char('*'); QStringMatcher matcher(fp.filePath, cs);
QRegExp regexp(asterisk + fp.filePath+ asterisk, Qt::CaseInsensitive, QRegExp::Wildcard); QRegExp regexp(fp.filePath, cs, QRegExp::Wildcard);
if (!regexp.isValid()) { if (!regexp.isValid()) {
d->m_current.clear(); // free memory d->m_current.clear(); // free memory
return betterEntries; return betterEntries;
} }
const QChar pathSeparator(QLatin1Char('/')); const QChar pathSeparator(QLatin1Char('/'));
const bool hasPathSeparator = fp.filePath.contains(pathSeparator); const bool hasPathSeparator = fp.filePath.contains(pathSeparator);
const bool hasWildcard = fp.filePath.contains(asterisk) || fp.filePath.contains(QLatin1Char('?')); const bool hasWildcard = containsWildcard(fp.filePath);
const bool containsPreviousEntry = !d->m_current.previousEntry.isEmpty() const bool containsPreviousEntry = !d->m_current.previousEntry.isEmpty()
&& fp.filePath.contains(d->m_current.previousEntry); && fp.filePath.contains(d->m_current.previousEntry);
const bool pathSeparatorAdded = !d->m_current.previousEntry.contains(pathSeparator) const bool pathSeparatorAdded = !d->m_current.previousEntry.contains(pathSeparator)
@@ -124,7 +124,6 @@ QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<LocatorFil
d->m_current.previousResultPaths.clear(); d->m_current.previousResultPaths.clear();
d->m_current.previousResultNames.clear(); d->m_current.previousResultNames.clear();
d->m_current.previousEntry = fp.filePath; d->m_current.previousEntry = fp.filePath;
const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(fp.filePath);
d->m_current.iterator->toFront(); d->m_current.iterator->toFront();
bool canceled = false; bool canceled = false;
while (d->m_current.iterator->hasNext()) { while (d->m_current.iterator->hasNext()) {
@@ -137,16 +136,32 @@ QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<LocatorFil
QString path = d->m_current.iterator->filePath(); QString path = d->m_current.iterator->filePath();
QString name = d->m_current.iterator->fileName(); QString name = d->m_current.iterator->fileName();
QString matchText = hasPathSeparator ? path : name; QString matchText = hasPathSeparator ? path : name;
if ((hasWildcard && regexp.exactMatch(matchText)) int index = hasWildcard ? regexp.indexIn(matchText) : matcher.indexIn(matchText);
|| (!hasWildcard && matcher.indexIn(matchText) != -1)) {
if (index >= 0) {
QFileInfo fi(path); QFileInfo fi(path);
LocatorFilterEntry entry(this, fi.fileName(), QString(path + fp.postfix)); LocatorFilterEntry filterEntry(this, fi.fileName(), QString(path + fp.postfix));
entry.extraInfo = FileUtils::shortNativePath(FileName(fi)); filterEntry.fileName = path;
entry.fileName = path; filterEntry.extraInfo = FileUtils::shortNativePath(FileName(fi));
if (matchText.startsWith(fp.filePath, caseSensitivityForPrefix))
betterEntries.append(entry); LocatorFilterEntry::HighlightInfo::DataType hDataType = LocatorFilterEntry::HighlightInfo::DisplayName;
int length = hasWildcard ? regexp.matchedLength() : fp.filePath.length();
const bool betterMatch = index == 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);
hDataType = LocatorFilterEntry::HighlightInfo::ExtraInfo;
}
if (index >= 0)
filterEntry.highlightInfo = LocatorFilterEntry::HighlightInfo(index, length, hDataType);
if (betterMatch)
betterEntries.append(filterEntry);
else else
goodEntries.append(entry); goodEntries.append(filterEntry);
d->m_current.previousResultPaths.append(path); d->m_current.previousResultPaths.append(path);
d->m_current.previousResultNames.append(name); d->m_current.previousResultNames.append(name);
} }

View File

@@ -67,20 +67,27 @@ QList<LocatorFilterEntry> CommandLocator::matchesFor(QFutureInterface<LocatorFil
// Get active, enabled actions matching text, store in list. // Get active, enabled actions matching text, store in list.
// Reference via index in extraInfo. // Reference via index in extraInfo.
const QChar ampersand = QLatin1Char('&'); const QChar ampersand = QLatin1Char('&');
const Qt::CaseSensitivity caseSensitivity_ = caseSensitivity(entry); const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
const int count = d->commands.size(); const int count = d->commands.size();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
if (future.isCanceled()) if (future.isCanceled())
break; break;
if (d->commands.at(i)->isActive()) { if (!d->commands.at(i)->isActive())
if (QAction *action = d->commands.at(i)->action()) continue;
if (action->isEnabled()) {
QString text = action->text(); QAction *action = d->commands.at(i)->action();
text.remove(ampersand); if (action && action->isEnabled()) {
if (text.startsWith(entry, caseSensitivity_)) QString text = action->text();
betterEntries.append(LocatorFilterEntry(this, text, QVariant(i))); text.remove(ampersand);
else if (text.contains(entry, caseSensitivity_)) const int index = text.indexOf(entry, 0, entryCaseSensitivity);
goodEntries.append(LocatorFilterEntry(this, text, QVariant(i))); if (index >= 0) {
LocatorFilterEntry filterEntry(this, text, QVariant(i));
filterEntry.highlightInfo = {index, entry.length()};
if (index == 0)
betterEntries.append(filterEntry);
else
goodEntries.append(filterEntry);
} }
} }
} }

View File

@@ -60,16 +60,20 @@ QList<LocatorFilterEntry> ExecuteFilter::matchesFor(QFutureInterface<LocatorFilt
if (!entry.isEmpty()) // avoid empty entry if (!entry.isEmpty()) // avoid empty entry
value.append(LocatorFilterEntry(this, entry, QVariant())); value.append(LocatorFilterEntry(this, entry, QVariant()));
QList<LocatorFilterEntry> others; QList<LocatorFilterEntry> others;
const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry); const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
foreach (const QString &i, m_commandHistory) { foreach (const QString &cmd, m_commandHistory) {
if (future.isCanceled()) if (future.isCanceled())
break; break;
if (i == entry) // avoid repeated entry if (cmd == entry) // avoid repeated entry
continue; continue;
if (i.startsWith(entry, caseSensitivityForPrefix)) LocatorFilterEntry filterEntry(this, cmd, QVariant());
value.append(LocatorFilterEntry(this, i, QVariant())); const int index = cmd.indexOf(entry, 0, entryCaseSensitivity);
else if (index >= 0) {
others.append(LocatorFilterEntry(this, i, QVariant())); filterEntry.highlightInfo = {index, entry.length()};
value.append(filterEntry);
} else {
others.append(filterEntry);
}
} }
value.append(others); value.append(others);
return value; return value;

View File

@@ -64,17 +64,23 @@ void ExternalToolsFilter::refresh(QFutureInterface<void> &)
void ExternalToolsFilter::prepareSearch(const QString &entry) void ExternalToolsFilter::prepareSearch(const QString &entry)
{ {
m_results.clear(); m_results.clear();
const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
Qt::CaseSensitivity useCaseSensitivity = caseSensitivity(entry);
const QMap<QString, ExternalTool *> externalToolsById = ExternalToolManager::toolsById(); const QMap<QString, ExternalTool *> externalToolsById = ExternalToolManager::toolsById();
auto end = externalToolsById.cend(); auto end = externalToolsById.cend();
for (auto it = externalToolsById.cbegin(); it != end; ++it) { for (auto it = externalToolsById.cbegin(); it != end; ++it) {
ExternalTool *tool = *it; ExternalTool *tool = *it;
if (tool->description().contains(entry, useCaseSensitivity) ||
tool->displayName().contains(entry, useCaseSensitivity)) {
int index = tool->displayName().indexOf(entry, 0, entryCaseSensitivity);
LocatorFilterEntry::HighlightInfo::DataType hDataType = LocatorFilterEntry::HighlightInfo::DisplayName;
if (index < 0) {
index = tool->description().indexOf(entry, 0, entryCaseSensitivity);
hDataType = LocatorFilterEntry::HighlightInfo::ExtraInfo;
}
if (index >= 0) {
LocatorFilterEntry filterEntry(this, tool->displayName(), QVariant::fromValue(tool)); LocatorFilterEntry filterEntry(this, tool->displayName(), QVariant::fromValue(tool));
filterEntry.extraInfo = tool->description(); filterEntry.extraInfo = tool->description();
filterEntry.highlightInfo = LocatorFilterEntry::HighlightInfo(index, entry.length(), hDataType);
m_results.append(filterEntry); m_results.append(filterEntry);
} }
} }

View File

@@ -41,11 +41,16 @@ namespace {
QList<LocatorFilterEntry> *categorize(const QString &entry, const QString &candidate, QList<LocatorFilterEntry> *categorize(const QString &entry, const QString &candidate,
Qt::CaseSensitivity caseSensitivity, Qt::CaseSensitivity caseSensitivity,
QList<LocatorFilterEntry> *betterEntries, QList<LocatorFilterEntry> *goodEntries) QList<LocatorFilterEntry> *betterEntries, QList<LocatorFilterEntry> *goodEntries,
int *index)
{ {
if (entry.isEmpty() || candidate.startsWith(entry, caseSensitivity)) const int position = candidate.indexOf(entry, 0, caseSensitivity);
if (index)
*index = position;
if (entry.isEmpty() || position == 0)
return betterEntries; return betterEntries;
else if (candidate.contains(entry, caseSensitivity)) else if (position >= 0)
return goodEntries; return goodEntries;
return 0; return 0;
} }
@@ -99,11 +104,15 @@ QList<LocatorFilterEntry> FileSystemFilter::matchesFor(QFutureInterface<LocatorF
foreach (const QString &dir, dirs) { foreach (const QString &dir, dirs) {
if (future.isCanceled()) if (future.isCanceled())
break; break;
if (QList<LocatorFilterEntry> *category = categorize(entryFileName, dir, caseSensitivity_, &betterEntries, int index = -1;
&goodEntries)) { if (QList<LocatorFilterEntry> *category = categorize(entryFileName, dir, caseSensitivity_,
&betterEntries, &goodEntries, &index)) {
const QString fullPath = dirInfo.filePath(dir); const QString fullPath = dirInfo.filePath(dir);
LocatorFilterEntry filterEntry(this, dir, QVariant()); LocatorFilterEntry filterEntry(this, dir, QVariant());
filterEntry.fileName = fullPath; filterEntry.fileName = fullPath;
if (index >= 0)
filterEntry.highlightInfo = {index, entryFileName.length()};
category->append(filterEntry); category->append(filterEntry);
} }
} }
@@ -113,11 +122,15 @@ QList<LocatorFilterEntry> FileSystemFilter::matchesFor(QFutureInterface<LocatorF
foreach (const QString &file, files) { foreach (const QString &file, files) {
if (future.isCanceled()) if (future.isCanceled())
break; break;
if (QList<LocatorFilterEntry> *category = categorize(fileName, file, caseSensitivity_, &betterEntries, int index = -1;
&goodEntries)) { if (QList<LocatorFilterEntry> *category = categorize(fileName, file, caseSensitivity_,
&betterEntries, &goodEntries, &index)) {
const QString fullPath = dirInfo.filePath(file); const QString fullPath = dirInfo.filePath(file);
LocatorFilterEntry filterEntry(this, file, QString(fullPath + fp.postfix)); LocatorFilterEntry filterEntry(this, file, QString(fullPath + fp.postfix));
filterEntry.fileName = fullPath; filterEntry.fileName = fullPath;
if (index >= 0)
filterEntry.highlightInfo = {index, fileName.length()};
category->append(filterEntry); category->append(filterEntry);
} }
} }

View File

@@ -118,27 +118,16 @@ bool ILocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
return false; return false;
} }
QString ILocatorFilter::trimWildcards(const QString &str)
{
if (str.isEmpty())
return str;
int first = 0, last = str.size() - 1;
const QChar asterisk = QLatin1Char('*');
const QChar question = QLatin1Char('?');
while (first < str.size() && (str.at(first) == asterisk || str.at(first) == question))
++first;
while (last >= 0 && (str.at(last) == asterisk || str.at(last) == question))
--last;
if (first > last)
return QString();
return str.mid(first, last - first + 1);
}
Qt::CaseSensitivity ILocatorFilter::caseSensitivity(const QString &str) Qt::CaseSensitivity ILocatorFilter::caseSensitivity(const QString &str)
{ {
return str == str.toLower() ? Qt::CaseInsensitive : Qt::CaseSensitive; return str == str.toLower() ? Qt::CaseInsensitive : Qt::CaseSensitive;
} }
bool ILocatorFilter::containsWildcard(const QString &str)
{
return str.contains(QLatin1Char('*')) || str.contains(QLatin1Char('?'));
}
QString ILocatorFilter::msgConfigureDialogTitle() QString ILocatorFilter::msgConfigureDialogTitle()
{ {
return tr("Filter Configuration"); return tr("Filter Configuration");

View File

@@ -37,6 +37,23 @@ class ILocatorFilter;
struct LocatorFilterEntry struct LocatorFilterEntry
{ {
struct HighlightInfo {
enum DataType {
DisplayName,
ExtraInfo
};
HighlightInfo(int startIndex, int length, DataType type = DataType::DisplayName)
: startIndex(startIndex)
, length(length)
, dataType(type)
{}
int startIndex;
int length;
DataType dataType;
};
LocatorFilterEntry() = default; LocatorFilterEntry() = default;
LocatorFilterEntry(ILocatorFilter *fromFilter, const QString &name, const QVariant &data, LocatorFilterEntry(ILocatorFilter *fromFilter, const QString &name, const QVariant &data,
@@ -67,6 +84,14 @@ struct LocatorFilterEntry
QString fileName; QString fileName;
/* internal */ /* internal */
bool fileIconResolved = false; bool fileIconResolved = false;
/* highlighting support */
HighlightInfo highlightInfo{0, 0};
static bool compareLexigraphically(const Core::LocatorFilterEntry &lhs,
const Core::LocatorFilterEntry &rhs)
{
return lhs.displayName < rhs.displayName;
}
}; };
class CORE_EXPORT ILocatorFilter : public QObject class CORE_EXPORT ILocatorFilter : public QObject
@@ -134,8 +159,8 @@ public:
/* Returns whether the filter should be enabled and used in menus. */ /* Returns whether the filter should be enabled and used in menus. */
bool isEnabled() const; bool isEnabled() const;
static QString trimWildcards(const QString &str);
static Qt::CaseSensitivity caseSensitivity(const QString &str); static Qt::CaseSensitivity caseSensitivity(const QString &str);
static bool containsWildcard(const QString &str);
static QString msgConfigureDialogTitle(); static QString msgConfigureDialogTitle();
static QString msgPrefixLabel(); static QString msgPrefixLabel();

View File

@@ -33,6 +33,8 @@
#include <coreplugin/modemanager.h> #include <coreplugin/modemanager.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/fileiconprovider.h> #include <coreplugin/fileiconprovider.h>
#include <coreplugin/find/searchresulttreeitemdelegate.h>
#include <coreplugin/find/searchresulttreeitemroles.h>
#include <coreplugin/icontext.h> #include <coreplugin/icontext.h>
#include <coreplugin/mainwindow.h> #include <coreplugin/mainwindow.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
@@ -69,8 +71,16 @@ namespace Internal {
class LocatorModel : public QAbstractListModel class LocatorModel : public QAbstractListModel
{ {
public: public:
enum Columns {
DisplayNameColumn,
ExtraInfoColumn,
ColumnCount
};
LocatorModel(QObject *parent = 0) LocatorModel(QObject *parent = 0)
: QAbstractListModel(parent) : QAbstractListModel(parent)
, mBackgroundColor(Utils::creatorTheme()->color(Utils::Theme::TextColorHighlightBackground).name())
{} {}
void clear(); void clear();
@@ -83,6 +93,7 @@ public:
private: private:
mutable QList<LocatorFilterEntry> mEntries; mutable QList<LocatorFilterEntry> mEntries;
bool hasExtraInfo = false; bool hasExtraInfo = false;
QColor mBackgroundColor;
}; };
class CompletionList : public QTreeView class CompletionList : public QTreeView
@@ -144,7 +155,7 @@ int LocatorModel::columnCount(const QModelIndex &parent) const
{ {
if (parent.isValid()) if (parent.isValid())
return 0; return 0;
return hasExtraInfo ? 2 : 1; return hasExtraInfo ? ColumnCount : 1;
} }
QVariant LocatorModel::data(const QModelIndex &index, int role) const QVariant LocatorModel::data(const QModelIndex &index, int role) const
@@ -154,9 +165,9 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole:
if (index.column() == 0) if (index.column() == DisplayNameColumn)
return mEntries.at(index.row()).displayName; return mEntries.at(index.row()).displayName;
else if (index.column() == 1) else if (index.column() == ExtraInfoColumn)
return mEntries.at(index.row()).extraInfo; return mEntries.at(index.row()).extraInfo;
break; break;
case Qt::ToolTipRole: case Qt::ToolTipRole:
@@ -167,7 +178,8 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
+ QLatin1String("\n\n") + mEntries.at(index.row()).extraInfo); + QLatin1String("\n\n") + mEntries.at(index.row()).extraInfo);
break; break;
case Qt::DecorationRole: case Qt::DecorationRole:
if (index.column() == 0) { case ItemDataRoles::ResultIconRole:
if (index.column() == DisplayNameColumn) {
LocatorFilterEntry &entry = mEntries[index.row()]; LocatorFilterEntry &entry = mEntries[index.row()];
if (!entry.fileIconResolved && !entry.fileName.isEmpty() && entry.displayIcon.isNull()) { if (!entry.fileIconResolved && !entry.fileName.isEmpty() && entry.displayIcon.isNull()) {
entry.fileIconResolved = true; entry.fileIconResolved = true;
@@ -177,11 +189,25 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
} }
break; break;
case Qt::ForegroundRole: case Qt::ForegroundRole:
if (index.column() == 1) if (index.column() == ExtraInfoColumn)
return QColor(Qt::darkGray); return QColor(Qt::darkGray);
break; break;
case Qt::UserRole: case ItemDataRoles::ResultItemRole:
return qVariantFromValue(mEntries.at(index.row())); return qVariantFromValue(mEntries.at(index.row()));
case ItemDataRoles::ResultBeginColumnNumberRole:
case ItemDataRoles::SearchTermLengthRole: {
LocatorFilterEntry &entry = mEntries[index.row()];
const int highlightColumn = entry.highlightInfo.dataType == LocatorFilterEntry::HighlightInfo::DisplayName
? DisplayNameColumn
: ExtraInfoColumn;
if (highlightColumn == index.column()) {
const bool startIndexRole = role == ItemDataRoles::ResultBeginColumnNumberRole;
return startIndexRole ? entry.highlightInfo.startIndex : entry.highlightInfo.length;
}
break;
}
case ItemDataRoles::ResultHighlightBackgroundColor:
return mBackgroundColor;
} }
return QVariant(); return QVariant();
@@ -202,6 +228,7 @@ void LocatorModel::addEntries(const QList<LocatorFilterEntry> &entries)
CompletionList::CompletionList(QWidget *parent) CompletionList::CompletionList(QWidget *parent)
: QTreeView(parent) : QTreeView(parent)
{ {
setItemDelegate(new SearchResultTreeItemDelegate(0, this));
setRootIsDecorated(false); setRootIsDecorated(false);
setUniformRowHeights(true); setUniformRowHeights(true);
header()->hide(); header()->hide();
@@ -611,9 +638,10 @@ void LocatorWidget::acceptCurrentEntry()
const QModelIndex index = m_completionList->currentIndex(); const QModelIndex index = m_completionList->currentIndex();
if (!index.isValid()) if (!index.isValid())
return; return;
const LocatorFilterEntry entry = m_locatorModel->data(index, Qt::UserRole).value<LocatorFilterEntry>(); const LocatorFilterEntry entry = m_locatorModel->data(index, ItemDataRoles::ResultItemRole).value<LocatorFilterEntry>();
m_completionList->hide(); m_completionList->hide();
m_fileLineEdit->clearFocus(); m_fileLineEdit->clearFocus();
Q_ASSERT(entry.filter != nullptr);
entry.filter->accept(entry); entry.filter->accept(entry);
} }

View File

@@ -55,19 +55,16 @@ OpenDocumentsFilter::OpenDocumentsFilter()
this, &OpenDocumentsFilter::refreshInternally); this, &OpenDocumentsFilter::refreshInternally);
} }
QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry) QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
const QString &entry)
{ {
QList<LocatorFilterEntry> goodEntries; QList<LocatorFilterEntry> goodEntries;
QList<LocatorFilterEntry> betterEntries; QList<LocatorFilterEntry> betterEntries;
const EditorManager::FilePathInfo fp = EditorManager::splitLineAndColumnNumber(entry); const EditorManager::FilePathInfo fp = EditorManager::splitLineAndColumnNumber(entry);
const QChar asterisk = QLatin1Char('*'); QRegExp regexp(fp.filePath, caseSensitivity(fp.filePath), QRegExp::Wildcard);
QString pattern = QString(asterisk);
pattern += fp.filePath;
pattern += asterisk;
QRegExp regexp(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
if (!regexp.isValid()) if (!regexp.isValid())
return goodEntries; return goodEntries;
const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(fp.filePath);
foreach (const Entry &editorEntry, editors()) { foreach (const Entry &editorEntry, editors()) {
if (future.isCanceled()) if (future.isCanceled())
break; break;
@@ -75,13 +72,16 @@ QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Locat
if (fileName.isEmpty()) if (fileName.isEmpty())
continue; continue;
QString displayName = editorEntry.displayName; QString displayName = editorEntry.displayName;
if (regexp.exactMatch(displayName)) { const int index = regexp.indexIn(displayName);
LocatorFilterEntry fiEntry(this, displayName, QString(fileName + fp.postfix)); if (index >= 0) {
fiEntry.extraInfo = FileUtils::shortNativePath(FileName::fromString(fileName)); LocatorFilterEntry filterEntry(this, displayName, QString(fileName + fp.postfix));
fiEntry.fileName = fileName; filterEntry.extraInfo = FileUtils::shortNativePath(FileName::fromString(fileName));
QList<LocatorFilterEntry> &category = displayName.startsWith(fp.filePath, caseSensitivityForPrefix) filterEntry.fileName = fileName;
? betterEntries : goodEntries; filterEntry.highlightInfo = {index, regexp.matchedLength()};
category.append(fiEntry); if (index == 0)
betterEntries.append(filterEntry);
else
goodEntries.append(filterEntry);
} }
} }
betterEntries.append(goodEntries); betterEntries.append(goodEntries);

View File

@@ -62,18 +62,16 @@ CppCurrentDocumentFilter::CppCurrentDocumentFilter(CppTools::CppModelManager *ma
} }
QList<Core::LocatorFilterEntry> CppCurrentDocumentFilter::matchesFor( QList<Core::LocatorFilterEntry> CppCurrentDocumentFilter::matchesFor(
QFutureInterface<Core::LocatorFilterEntry> &future, const QString & origEntry) QFutureInterface<Core::LocatorFilterEntry> &future, const QString & entry)
{ {
QString entry = trimWildcards(origEntry);
QList<Core::LocatorFilterEntry> goodEntries; QList<Core::LocatorFilterEntry> goodEntries;
QList<Core::LocatorFilterEntry> betterEntries; QList<Core::LocatorFilterEntry> betterEntries;
QStringMatcher matcher(entry, Qt::CaseInsensitive); const Qt::CaseSensitivity cs = caseSensitivity(entry);
const QChar asterisk = QLatin1Char('*'); QStringMatcher matcher(entry, cs);
QRegExp regexp(asterisk + entry + asterisk, Qt::CaseInsensitive, QRegExp::Wildcard); QRegExp regexp(entry, cs, QRegExp::Wildcard);
if (!regexp.isValid()) if (!regexp.isValid())
return goodEntries; return goodEntries;
bool hasWildcard = (entry.contains(asterisk) || entry.contains(QLatin1Char('?'))); bool hasWildcard = containsWildcard(entry);
const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
foreach (IndexItem::Ptr info, itemsOfCurrentDocument()) { foreach (IndexItem::Ptr info, itemsOfCurrentDocument()) {
if (future.isCanceled()) if (future.isCanceled())
@@ -85,20 +83,30 @@ QList<Core::LocatorFilterEntry> CppCurrentDocumentFilter::matchesFor(
else if (info->type() == IndexItem::Function) else if (info->type() == IndexItem::Function)
matchString += info->symbolType(); matchString += info->symbolType();
if ((hasWildcard && regexp.exactMatch(matchString)) int index = hasWildcard ? regexp.indexIn(matchString) : matcher.indexIn(matchString);
|| (!hasWildcard && matcher.indexIn(matchString) != -1)) if (index >= 0) {
{ const bool betterMatch = index == 0;
QVariant id = qVariantFromValue(info); QVariant id = qVariantFromValue(info);
QString name = matchString; QString name = matchString;
QString extraInfo = info->symbolScope(); QString extraInfo = info->symbolScope();
if (info->type() == IndexItem::Function) { if (info->type() == IndexItem::Function) {
if (info->unqualifiedNameAndScope(matchString, &name, &extraInfo)) if (info->unqualifiedNameAndScope(matchString, &name, &extraInfo)) {
name += info->symbolType(); name += info->symbolType();
index = hasWildcard ? regexp.indexIn(name) : matcher.indexIn(name);
}
} }
Core::LocatorFilterEntry filterEntry(this, name, id, info->icon()); Core::LocatorFilterEntry filterEntry(this, name, id, info->icon());
filterEntry.extraInfo = extraInfo; filterEntry.extraInfo = extraInfo;
if (index < 0) {
if (matchString.startsWith(entry, caseSensitivityForPrefix)) index = hasWildcard ? regexp.indexIn(extraInfo) : matcher.indexIn(extraInfo);
filterEntry.highlightInfo.dataType = Core::LocatorFilterEntry::HighlightInfo::ExtraInfo;
}
if (index >= 0) {
filterEntry.highlightInfo.startIndex = index;
filterEntry.highlightInfo.length = hasWildcard ? regexp.matchedLength() : entry.length();
}
if (betterMatch)
betterEntries.append(filterEntry); betterEntries.append(filterEntry);
else else
goodEntries.append(filterEntry); goodEntries.append(filterEntry);

View File

@@ -27,6 +27,7 @@
#include "cppmodelmanager.h" #include "cppmodelmanager.h"
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <utils/algorithm.h>
#include <QRegExp> #include <QRegExp>
#include <QStringMatcher> #include <QStringMatcher>
@@ -66,26 +67,18 @@ void CppLocatorFilter::refresh(QFutureInterface<void> &future)
Q_UNUSED(future) Q_UNUSED(future)
} }
static bool compareLexigraphically(const Core::LocatorFilterEntry &a,
const Core::LocatorFilterEntry &b)
{
return a.displayName < b.displayName;
}
QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor( QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
QFutureInterface<Core::LocatorFilterEntry> &future, const QString &origEntry) QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
{ {
QString entry = trimWildcards(origEntry);
QList<Core::LocatorFilterEntry> goodEntries; QList<Core::LocatorFilterEntry> goodEntries;
QList<Core::LocatorFilterEntry> betterEntries; QList<Core::LocatorFilterEntry> betterEntries;
const QChar asterisk = QLatin1Char('*'); const Qt::CaseSensitivity cs = caseSensitivity(entry);
QStringMatcher matcher(entry, Qt::CaseInsensitive); QStringMatcher matcher(entry, cs);
QRegExp regexp(asterisk + entry+ asterisk, Qt::CaseInsensitive, QRegExp::Wildcard); QRegExp regexp(entry, cs, QRegExp::Wildcard);
if (!regexp.isValid()) if (!regexp.isValid())
return goodEntries; return goodEntries;
bool hasWildcard = (entry.contains(asterisk) || entry.contains(QLatin1Char('?'))); bool hasWildcard = containsWildcard(entry);
bool hasColonColon = entry.contains(QLatin1String("::")); bool hasColonColon = entry.contains(QLatin1String("::"));
const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
const IndexItem::ItemType wanted = matchTypes(); const IndexItem::ItemType wanted = matchTypes();
m_data->filterAllFiles([&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult { m_data->filterAllFiles([&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult {
@@ -93,10 +86,20 @@ QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
return IndexItem::Break; return IndexItem::Break;
if (info->type() & wanted) { if (info->type() & wanted) {
const QString matchString = hasColonColon ? info->scopedSymbolName() : info->symbolName(); const QString matchString = hasColonColon ? info->scopedSymbolName() : info->symbolName();
if ((hasWildcard && regexp.exactMatch(matchString)) || int index = hasWildcard ? regexp.indexIn(matchString) : matcher.indexIn(matchString);
(!hasWildcard && matcher.indexIn(matchString) != -1)) { if (index >= 0) {
const Core::LocatorFilterEntry filterEntry = filterEntryFromIndexItem(info); const bool betterMatch = index == 0;
if (matchString.startsWith(entry, caseSensitivityForPrefix)) 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)
betterEntries.append(filterEntry); betterEntries.append(filterEntry);
else else
goodEntries.append(filterEntry); goodEntries.append(filterEntry);
@@ -110,9 +113,9 @@ QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
}); });
if (goodEntries.size() < 1000) if (goodEntries.size() < 1000)
std::stable_sort(goodEntries.begin(), goodEntries.end(), compareLexigraphically); Utils::sort(goodEntries, Core::LocatorFilterEntry::compareLexigraphically);
if (betterEntries.size() < 1000) if (betterEntries.size() < 1000)
std::stable_sort(betterEntries.begin(), betterEntries.end(), compareLexigraphically); Utils::sort(betterEntries, Core::LocatorFilterEntry::compareLexigraphically);
betterEntries += goodEntries; betterEntries += goodEntries;
return betterEntries; return betterEntries;

View File

@@ -127,8 +127,12 @@ QList<LocatorFilterEntry> HelpIndexFilter::matchesFor(QFutureInterface<LocatorFi
keywords << unsortedKeywords; keywords << unsortedKeywords;
m_keywordCache = allresults; m_keywordCache = allresults;
m_searchTermCache = entry; m_searchTermCache = entry;
foreach (const QString &keyword, keywords) foreach (const QString &keyword, keywords) {
entries.append(LocatorFilterEntry(this, keyword, QVariant(), m_icon)); const int index = keyword.indexOf(entry, 0, cs);
LocatorFilterEntry filterEntry(this, keyword, QVariant(), m_icon);
filterEntry.highlightInfo = {index, entry.length()};
entries.append(filterEntry);
}
return entries; return entries;
} }

View File

@@ -129,15 +129,16 @@ RemoteHelpFilter::~RemoteHelpFilter()
{ {
} }
QList<Core::LocatorFilterEntry> RemoteHelpFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &pattern) QList<Core::LocatorFilterEntry> RemoteHelpFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
{ {
QList<Core::LocatorFilterEntry> entries; QList<Core::LocatorFilterEntry> entries;
foreach (const QString &url, remoteUrls()) { foreach (const QString &url, remoteUrls()) {
if (future.isCanceled()) if (future.isCanceled())
break; break;
const QString name = url.arg(entry);
entries.append(Core::LocatorFilterEntry(this, url.arg(pattern), QVariant(), Core::LocatorFilterEntry filterEntry(this, name, QVariant(), m_icon);
m_icon)); filterEntry.highlightInfo = {name.lastIndexOf(entry), entry.length()};
entries.append(filterEntry);
} }
return entries; return entries;
} }

View File

@@ -54,26 +54,32 @@ QList<Core::LocatorFilterEntry> MacroLocatorFilter::matchesFor(QFutureInterface<
QList<Core::LocatorFilterEntry> goodEntries; QList<Core::LocatorFilterEntry> goodEntries;
QList<Core::LocatorFilterEntry> betterEntries; QList<Core::LocatorFilterEntry> betterEntries;
const Qt::CaseSensitivity caseSensitivity_ = caseSensitivity(entry); const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
const QMap<QString, Macro*> &macros = MacroManager::macros(); const QMap<QString, Macro*> &macros = MacroManager::macros();
QMapIterator<QString, Macro*> it(macros); QMapIterator<QString, Macro*> it(macros);
while (it.hasNext()) { while (it.hasNext()) {
it.next(); it.next();
QString name = it.key(); const QString displayName = it.key();
const QString description = it.value()->description();
QList<Core::LocatorFilterEntry> *category = 0; int index = displayName.indexOf(entry, 0, entryCaseSensitivity);
if (name.startsWith(entry, caseSensitivity_)) Core::LocatorFilterEntry::HighlightInfo::DataType hDataType = Core::LocatorFilterEntry::HighlightInfo::DisplayName;
category = &betterEntries; if (index < 0) {
else if (name.contains(entry, caseSensitivity_)) index = description.indexOf(entry, 0, entryCaseSensitivity);
category = &goodEntries; hDataType = Core::LocatorFilterEntry::HighlightInfo::ExtraInfo;
}
if (category) { if (index >= 0) {
QVariant id; Core::LocatorFilterEntry filterEntry(this, displayName, QVariant(), m_icon);
Core::LocatorFilterEntry entry(this, it.key(), id, m_icon); filterEntry.extraInfo = description;
entry.extraInfo = it.value()->description(); filterEntry.highlightInfo = Core::LocatorFilterEntry::HighlightInfo(index, entry.length(), hDataType);
category->append(entry);
if (index == 0)
betterEntries.append(filterEntry);
else
goodEntries.append(filterEntry);
} }
} }
betterEntries.append(goodEntries); betterEntries.append(goodEntries);

View File

@@ -53,24 +53,18 @@ void FunctionFilter::refresh(QFutureInterface<void> &)
{ {
} }
static bool compareLexigraphically(const Core::LocatorFilterEntry &a, QList<Core::LocatorFilterEntry> FunctionFilter::matchesFor(
const Core::LocatorFilterEntry &b) QFutureInterface<Core::LocatorFilterEntry> &future,
const QString &entry)
{ {
return a.displayName < b.displayName;
}
QList<Core::LocatorFilterEntry> FunctionFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &origEntry)
{
QString entry = trimWildcards(origEntry);
QList<Core::LocatorFilterEntry> goodEntries; QList<Core::LocatorFilterEntry> goodEntries;
QList<Core::LocatorFilterEntry> betterEntries; QList<Core::LocatorFilterEntry> betterEntries;
const QChar asterisk = QLatin1Char('*'); const Qt::CaseSensitivity cs = caseSensitivity(entry);
QStringMatcher matcher(entry, Qt::CaseInsensitive); QStringMatcher matcher(entry, cs);
QRegExp regexp(asterisk + entry+ asterisk, Qt::CaseInsensitive, QRegExp::Wildcard); QRegExp regexp(entry, cs, QRegExp::Wildcard);
if (!regexp.isValid()) if (!regexp.isValid())
return goodEntries; return goodEntries;
bool hasWildcard = (entry.contains(asterisk) || entry.contains(QLatin1Char('?'))); bool hasWildcard = containsWildcard(entry);
const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
QHashIterator<QString, QList<LocatorData::Entry> > it(m_data->entries()); QHashIterator<QString, QList<LocatorData::Entry> > it(m_data->entries());
while (it.hasNext()) { while (it.hasNext()) {
@@ -83,14 +77,17 @@ QList<Core::LocatorFilterEntry> FunctionFilter::matchesFor(QFutureInterface<Core
foreach (const LocatorData::Entry &info, items) { foreach (const LocatorData::Entry &info, items) {
if (info.type != LocatorData::Function) if (info.type != LocatorData::Function)
continue; continue;
if ((hasWildcard && regexp.exactMatch(info.symbolName))
|| (!hasWildcard && matcher.indexIn(info.symbolName) != -1)) {
const int index = hasWildcard ? regexp.indexIn(info.symbolName)
: matcher.indexIn(info.symbolName);
if (index >= 0) {
QVariant id = qVariantFromValue(info); QVariant id = qVariantFromValue(info);
Core::LocatorFilterEntry filterEntry(this, info.displayName, id/*, info.icon*/); Core::LocatorFilterEntry filterEntry(this, info.displayName, id/*, info.icon*/);
filterEntry.extraInfo = info.extraInfo; filterEntry.extraInfo = info.extraInfo;
const int length = hasWildcard ? regexp.matchedLength() : entry.length();
filterEntry.highlightInfo = {index, length};
if (info.symbolName.startsWith(entry, caseSensitivityForPrefix)) if (index == 0)
betterEntries.append(filterEntry); betterEntries.append(filterEntry);
else else
goodEntries.append(filterEntry); goodEntries.append(filterEntry);
@@ -99,9 +96,9 @@ QList<Core::LocatorFilterEntry> FunctionFilter::matchesFor(QFutureInterface<Core
} }
if (goodEntries.size() < 1000) if (goodEntries.size() < 1000)
Utils::sort(goodEntries, compareLexigraphically); Utils::sort(goodEntries, Core::LocatorFilterEntry::compareLexigraphically);
if (betterEntries.size() < 1000) if (betterEntries.size() < 1000)
Utils::sort(betterEntries, compareLexigraphically); Utils::sort(betterEntries, Core::LocatorFilterEntry::compareLexigraphically);
betterEntries += goodEntries; betterEntries += goodEntries;
return betterEntries; return betterEntries;