Locator BaseFileFilter: Iterator based API for list of files

There are situations where we can generate the list of files on the fly in
the worker thread instead of generating the full file list in the UI
thread beforehand. Change the API to support that.

Change-Id: I331336f4b019184ba0da311b66e6283029c612c4
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
Eike Ziller
2014-12-10 11:29:33 +01:00
parent 94e8577852
commit 0eeb590fdf
7 changed files with 147 additions and 46 deletions

View File

@@ -32,6 +32,7 @@
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <QDir> #include <QDir>
#include <QStringMatcher> #include <QStringMatcher>
@@ -42,6 +43,11 @@ using namespace Utils;
BaseFileFilter::BaseFileFilter() BaseFileFilter::BaseFileFilter()
: m_forceNewSearchList(false) : m_forceNewSearchList(false)
{
setFileIterator(new ListIterator(QStringList()));
}
BaseFileFilter::~BaseFileFilter()
{ {
} }
@@ -59,32 +65,32 @@ QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<LocatorFil
const QChar pathSeparator(QLatin1Char('/')); const QChar pathSeparator(QLatin1Char('/'));
const bool hasPathSeparator = needle.contains(pathSeparator); const bool hasPathSeparator = needle.contains(pathSeparator);
const bool hasWildcard = needle.contains(asterisk) || needle.contains(QLatin1Char('?')); const bool hasWildcard = needle.contains(asterisk) || needle.contains(QLatin1Char('?'));
QStringList searchListPaths;
QStringList searchListNames;
const bool containsPreviousEntry = !m_previousEntry.isEmpty() const bool containsPreviousEntry = !m_previousEntry.isEmpty()
&& needle.contains(m_previousEntry); && needle.contains(m_previousEntry);
const bool pathSeparatorAdded = !m_previousEntry.contains(pathSeparator) const bool pathSeparatorAdded = !m_previousEntry.contains(pathSeparator)
&& needle.contains(pathSeparator); && needle.contains(pathSeparator);
if (!m_forceNewSearchList && containsPreviousEntry && !pathSeparatorAdded) { const bool searchInPreviousResults = !m_forceNewSearchList && containsPreviousEntry
searchListPaths = m_previousResultPaths; && !pathSeparatorAdded;
searchListNames = m_previousResultNames; QSharedPointer<Iterator> iterator;
} else { if (searchInPreviousResults)
searchListPaths = m_files; iterator.reset(new ListIterator(m_previousResultPaths, m_previousResultNames));
searchListNames = m_fileNames; else
} iterator = fileIterator();
QTC_ASSERT(iterator.data(), return QList<LocatorFilterEntry>());
m_previousResultPaths.clear(); m_previousResultPaths.clear();
m_previousResultNames.clear(); m_previousResultNames.clear();
m_forceNewSearchList = false; m_forceNewSearchList = false;
m_previousEntry = needle; m_previousEntry = needle;
const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(needle); const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(needle);
QStringListIterator paths(searchListPaths); iterator->toFront();
QStringListIterator names(searchListNames); while (iterator->hasNext()) {
while (paths.hasNext() && names.hasNext()) {
if (future.isCanceled()) if (future.isCanceled())
break; break;
QString path = paths.next(); iterator->next();
QString name = names.next(); QString path = iterator->filePath();
QString name = iterator->fileName();
QString matchText = hasPathSeparator ? path : name; QString matchText = hasPathSeparator ? path : name;
if ((hasWildcard && regexp.exactMatch(matchText)) if ((hasWildcard && regexp.exactMatch(matchText))
|| (!hasWildcard && matcher.indexIn(matchText) != -1)) { || (!hasWildcard && matcher.indexIn(matchText) != -1)) {
@@ -111,12 +117,79 @@ void BaseFileFilter::accept(LocatorFilterEntry selection) const
EditorManager::CanContainLineNumber); EditorManager::CanContainLineNumber);
} }
void BaseFileFilter::generateFileNames() void BaseFileFilter::invalidateCachedResults()
{ {
m_fileNames.clear(); m_forceNewSearchList = true;
foreach (const QString &fileName, m_files) { m_previousEntry.clear();
QFileInfo fi(fileName); m_previousResultPaths.clear();
m_previousResultNames.clear();
}
/*!
Takes ownership of the \a iterator. The previously set iterator might not be deleted until
a currently running search is finished.
*/
void BaseFileFilter::setFileIterator(BaseFileFilter::Iterator *iterator)
{
invalidateCachedResults();
m_iterator.reset(iterator);
}
QSharedPointer<BaseFileFilter::Iterator> BaseFileFilter::fileIterator()
{
return m_iterator;
}
BaseFileFilter::ListIterator::ListIterator(const QStringList &filePaths)
{
m_filePaths = filePaths;
foreach (const QString &path, m_filePaths) {
QFileInfo fi(path);
m_fileNames.append(fi.fileName()); m_fileNames.append(fi.fileName());
} }
m_forceNewSearchList = true; toFront();
}
BaseFileFilter::ListIterator::ListIterator(const QStringList &filePaths,
const QStringList &fileNames)
{
m_filePaths = filePaths;
m_fileNames = fileNames;
toFront();
}
void BaseFileFilter::ListIterator::toFront()
{
m_pathPosition = m_filePaths.constBegin() - 1;
m_namePosition = m_fileNames.constBegin() - 1;
}
bool BaseFileFilter::ListIterator::hasNext() const
{
QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return false);
return m_pathPosition + 1 != m_filePaths.constEnd();
}
QString BaseFileFilter::ListIterator::next()
{
QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return QString());
QTC_ASSERT(m_namePosition != m_fileNames.constEnd(), return QString());
++m_pathPosition;
++m_namePosition;
QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return QString());
QTC_ASSERT(m_namePosition != m_fileNames.constEnd(), return QString());
return *m_pathPosition;
}
QString BaseFileFilter::ListIterator::filePath() const
{
QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return QString());
return *m_pathPosition;
}
QString BaseFileFilter::ListIterator::fileName() const
{
QTC_ASSERT(m_namePosition != m_fileNames.constEnd(), return QString());
return *m_namePosition;
} }

View File

@@ -33,6 +33,7 @@
#include "ilocatorfilter.h" #include "ilocatorfilter.h"
#include <QSharedPointer>
#include <QStringList> #include <QStringList>
namespace Core { namespace Core {
@@ -42,21 +43,47 @@ class CORE_EXPORT BaseFileFilter : public ILocatorFilter
Q_OBJECT Q_OBJECT
public: public:
class Iterator {
public:
virtual ~Iterator() { }
virtual void toFront() = 0;
virtual bool hasNext() const = 0;
virtual QString next() = 0;
virtual QString filePath() const = 0;
virtual QString fileName() const = 0;
};
class ListIterator : public Iterator {
public:
ListIterator(const QStringList &filePaths);
ListIterator(const QStringList &filePaths, const QStringList &fileNames);
void toFront();
bool hasNext() const;
QString next();
QString filePath() const;
QString fileName() const;
private:
QStringList m_filePaths;
QStringList m_fileNames;
QStringList::const_iterator m_pathPosition;
QStringList::const_iterator m_namePosition;
};
BaseFileFilter(); BaseFileFilter();
~BaseFileFilter();
QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry); QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry);
void accept(LocatorFilterEntry selection) const; void accept(LocatorFilterEntry selection) const;
protected: protected:
/* Generates the file names from the list of file paths in m_files. */ void invalidateCachedResults();
void generateFileNames();
/* Subclasses should update the file list latest in their prepareSearch method. */ void setFileIterator(Iterator *iterator);
inline QStringList &files() { return m_files; } QSharedPointer<Iterator> fileIterator();
inline const QStringList &files() const { return m_files; }
private: private:
QStringList m_files; QSharedPointer<Iterator> m_iterator;
QStringList m_fileNames;
QStringList m_previousResultPaths; QStringList m_previousResultPaths;
QStringList m_previousResultNames; QStringList m_previousResultNames;
bool m_forceNewSearchList; bool m_forceNewSearchList;

View File

@@ -59,7 +59,7 @@ QByteArray DirectoryFilter::saveState() const
out << m_filters; out << m_filters;
out << shortcutString(); out << shortcutString();
out << isIncludedByDefault(); out << isIncludedByDefault();
out << files(); out << m_files;
return value; return value;
} }
@@ -77,13 +77,13 @@ bool DirectoryFilter::restoreState(const QByteArray &state)
in >> m_filters; in >> m_filters;
in >> shortcut; in >> shortcut;
in >> defaultFilter; in >> defaultFilter;
in >> files(); in >> m_files;
setDisplayName(name); setDisplayName(name);
setShortcutString(shortcut); setShortcutString(shortcut);
setIncludedByDefault(defaultFilter); setIncludedByDefault(defaultFilter);
generateFileNames(); setFileIterator(new BaseFileFilter::ListIterator(m_files));
return true; return true;
} }
@@ -175,8 +175,8 @@ void DirectoryFilter::refresh(QFutureInterface<void> &future)
{ {
QMutexLocker locker(&m_lock); QMutexLocker locker(&m_lock);
if (m_directories.count() < 1) { if (m_directories.count() < 1) {
files().clear(); m_files.clear();
generateFileNames(); setFileIterator(new BaseFileFilter::ListIterator(m_files));
future.setProgressRange(0, 1); future.setProgressRange(0, 1);
future.setProgressValueAndText(1, tr("%1 filter update: 0 files").arg(displayName())); future.setProgressValueAndText(1, tr("%1 filter update: 0 files").arg(displayName()));
return; return;
@@ -197,8 +197,8 @@ void DirectoryFilter::refresh(QFutureInterface<void> &future)
if (!future.isCanceled()) { if (!future.isCanceled()) {
QMutexLocker locker(&m_lock); QMutexLocker locker(&m_lock);
files() = filesFound; m_files = filesFound;
generateFileNames(); setFileIterator(new BaseFileFilter::ListIterator(m_files));
future.setProgressValue(it.maxProgress()); future.setProgressValue(it.maxProgress());
} else { } else {
future.setProgressValueAndText(it.currentProgress(), tr("%1 filter update: canceled").arg(displayName())); future.setProgressValueAndText(it.currentProgress(), tr("%1 filter update: canceled").arg(displayName()));

View File

@@ -67,6 +67,7 @@ private:
QDialog *m_dialog; QDialog *m_dialog;
Ui::DirectoryFilterOptions m_ui; Ui::DirectoryFilterOptions m_ui;
mutable QMutex m_lock; mutable QMutex m_lock;
QStringList m_files;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -52,9 +52,7 @@ class MyBaseFileFilter : public Core::BaseFileFilter
public: public:
MyBaseFileFilter(const QStringList &theFiles) MyBaseFileFilter(const QStringList &theFiles)
{ {
files().clear(); setFileIterator(new BaseFileFilter::ListIterator(theFiles));
files().append(theFiles);
BaseFileFilter::generateFileNames();
} }
void refresh(QFutureInterface<void> &) {} void refresh(QFutureInterface<void> &) {}

View File

@@ -56,6 +56,7 @@ void AllProjectsFilter::markFilesAsOutOfDate()
{ {
QMutexLocker lock(&m_mutex); Q_UNUSED(lock) QMutexLocker lock(&m_mutex); Q_UNUSED(lock)
m_filesUpToDate = false; m_filesUpToDate = false;
invalidateCachedResults();
} }
void AllProjectsFilter::prepareSearch(const QString &entry) void AllProjectsFilter::prepareSearch(const QString &entry)
@@ -64,11 +65,11 @@ void AllProjectsFilter::prepareSearch(const QString &entry)
QMutexLocker lock(&m_mutex); Q_UNUSED(lock) QMutexLocker lock(&m_mutex); Q_UNUSED(lock)
if (m_filesUpToDate) if (m_filesUpToDate)
return; return;
files().clear(); QStringList paths;
foreach (Project *project, SessionManager::projects()) foreach (Project *project, SessionManager::projects())
files().append(project->files(Project::AllFiles)); paths.append(project->files(Project::AllFiles));
Utils::sort(files()); Utils::sort(paths);
generateFileNames(); setFileIterator(new BaseFileFilter::ListIterator(paths));
m_filesUpToDate = true; m_filesUpToDate = true;
} }

View File

@@ -56,6 +56,7 @@ void CurrentProjectFilter::markFilesAsOutOfDate()
{ {
QMutexLocker lock(&m_filesUpToDateMutex); Q_UNUSED(lock) QMutexLocker lock(&m_filesUpToDateMutex); Q_UNUSED(lock)
m_filesUpToDate = false; m_filesUpToDate = false;
invalidateCachedResults();
} }
void CurrentProjectFilter::prepareSearch(const QString &entry) void CurrentProjectFilter::prepareSearch(const QString &entry)
@@ -64,13 +65,13 @@ void CurrentProjectFilter::prepareSearch(const QString &entry)
QMutexLocker lock(&m_filesUpToDateMutex); Q_UNUSED(lock) QMutexLocker lock(&m_filesUpToDateMutex); Q_UNUSED(lock)
if (m_filesUpToDate) if (m_filesUpToDate)
return; return;
files().clear();
m_filesUpToDate = true; m_filesUpToDate = true;
if (!m_project) QStringList paths;
return; if (m_project) {
files() = m_project->files(Project::AllFiles); paths = m_project->files(Project::AllFiles);
Utils::sort(files()); Utils::sort(paths);
generateFileNames(); }
setFileIterator(new BaseFileFilter::ListIterator(paths));
} }
void CurrentProjectFilter::currentProjectChanged(ProjectExplorer::Project *project) void CurrentProjectFilter::currentProjectChanged(ProjectExplorer::Project *project)