forked from qt-creator/qt-creator
QmlProject: Don't use RegExp for simple wildcard matching
QRegExp matching is quite expensive, and has to be done for every file in the project directory tree against all possible suffixes. Optimize for the common case that the pattern is "*.suffix" by doing a fileName.endsWidth(suffix) in this case. This speeds up loading of examples/declarative/declarative.qmlproject by about 30%. Reviewed-by: Christian Kamm
This commit is contained in:
@@ -47,7 +47,21 @@ void FileFilterBaseItem::setFilter(const QString &filter)
|
|||||||
m_filter = filter;
|
m_filter = filter;
|
||||||
|
|
||||||
m_regExpList.clear();
|
m_regExpList.clear();
|
||||||
|
m_fileSuffixes.clear();
|
||||||
|
|
||||||
foreach (const QString &pattern, filter.split(QLatin1Char(';'))) {
|
foreach (const QString &pattern, filter.split(QLatin1Char(';'))) {
|
||||||
|
if (pattern.isEmpty())
|
||||||
|
continue;
|
||||||
|
// decide if it's a canonical pattern like *.x
|
||||||
|
if (pattern.startsWith(QLatin1String("*."))) {
|
||||||
|
const QString suffix = pattern.right(pattern.size() - 1);
|
||||||
|
if (!suffix.contains(QLatin1Char('*'))
|
||||||
|
&& !suffix.contains(QLatin1Char('?'))
|
||||||
|
&& !suffix.contains(QLatin1Char('['))) {
|
||||||
|
m_fileSuffixes << suffix;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
m_regExpList << QRegExp(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
|
m_regExpList << QRegExp(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,16 +127,9 @@ bool FileFilterBaseItem::matchesFile(const QString &filePath) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool regexMatches = false;
|
|
||||||
const QString &fileName = QFileInfo(filePath).fileName();
|
const QString &fileName = QFileInfo(filePath).fileName();
|
||||||
foreach (const QRegExp &exp, m_regExpList) {
|
|
||||||
if (exp.exactMatch(fileName)) {
|
|
||||||
regexMatches = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!regexMatches)
|
if (!fileMatches(fileName))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const QDir fileDir = QFileInfo(filePath).absoluteDir();
|
const QDir fileDir = QFileInfo(filePath).absoluteDir();
|
||||||
@@ -164,7 +171,7 @@ void FileFilterBaseItem::updateFileList()
|
|||||||
foreach (const QString &explicitPath, m_explicitFiles) {
|
foreach (const QString &explicitPath, m_explicitFiles) {
|
||||||
newFiles << absolutePath(explicitPath);
|
newFiles << absolutePath(explicitPath);
|
||||||
}
|
}
|
||||||
if (!m_regExpList.isEmpty() && m_explicitFiles.isEmpty())
|
if ((!m_fileSuffixes.isEmpty() || !m_regExpList.isEmpty()) && m_explicitFiles.isEmpty())
|
||||||
newFiles += filesInSubTree(QDir(m_defaultDir), QDir(projectDir), &dirsToBeWatched);
|
newFiles += filesInSubTree(QDir(m_defaultDir), QDir(projectDir), &dirsToBeWatched);
|
||||||
|
|
||||||
if (newFiles != m_files) {
|
if (newFiles != m_files) {
|
||||||
@@ -190,6 +197,23 @@ void FileFilterBaseItem::updateFileList()
|
|||||||
m_dirWatcher.addDirectories(watchDirs.toList());
|
m_dirWatcher.addDirectories(watchDirs.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FileFilterBaseItem::fileMatches(const QString &fileName) const
|
||||||
|
{
|
||||||
|
foreach (const QString &suffix, m_fileSuffixes) {
|
||||||
|
if (fileName.endsWith(suffix, Qt::CaseInsensitive)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (const QRegExp &filter, m_regExpList) {
|
||||||
|
if (filter.exactMatch(fileName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QSet<QString> FileFilterBaseItem::filesInSubTree(const QDir &rootDir, const QDir &dir, QSet<QString> *parsedDirs)
|
QSet<QString> FileFilterBaseItem::filesInSubTree(const QDir &rootDir, const QDir &dir, QSet<QString> *parsedDirs)
|
||||||
{
|
{
|
||||||
QSet<QString> fileSet;
|
QSet<QString> fileSet;
|
||||||
@@ -199,12 +223,9 @@ QSet<QString> FileFilterBaseItem::filesInSubTree(const QDir &rootDir, const QDir
|
|||||||
|
|
||||||
foreach (const QFileInfo &file, dir.entryInfoList(QDir::Files)) {
|
foreach (const QFileInfo &file, dir.entryInfoList(QDir::Files)) {
|
||||||
const QString fileName = file.fileName();
|
const QString fileName = file.fileName();
|
||||||
foreach (const QRegExp &filter, m_regExpList) {
|
|
||||||
if (filter.exactMatch(fileName)) {
|
if (fileMatches(fileName))
|
||||||
fileSet.insert(file.absoluteFilePath());
|
fileSet.insert(file.absoluteFilePath());
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recursive()) {
|
if (recursive()) {
|
||||||
|
|||||||
@@ -54,12 +54,15 @@ private:
|
|||||||
QString absolutePath(const QString &path) const;
|
QString absolutePath(const QString &path) const;
|
||||||
QString absoluteDir() const;
|
QString absoluteDir() const;
|
||||||
|
|
||||||
|
bool fileMatches(const QString &fileName) const;
|
||||||
QSet<QString> filesInSubTree(const QDir &rootDir, const QDir &dir, QSet<QString> *parsedDirs = 0);
|
QSet<QString> filesInSubTree(const QDir &rootDir, const QDir &dir, QSet<QString> *parsedDirs = 0);
|
||||||
|
|
||||||
QString m_rootDir;
|
QString m_rootDir;
|
||||||
QString m_defaultDir;
|
QString m_defaultDir;
|
||||||
|
|
||||||
QString m_filter;
|
QString m_filter;
|
||||||
|
// simple "*.png" patterns are stored in m_fileSuffixes, otherwise store in m_regExpList
|
||||||
|
QList<QString> m_fileSuffixes;
|
||||||
QList<QRegExp> m_regExpList;
|
QList<QRegExp> m_regExpList;
|
||||||
|
|
||||||
enum RecursiveOption {
|
enum RecursiveOption {
|
||||||
|
|||||||
@@ -208,6 +208,35 @@ void tst_FileFormat::testFileFilter()
|
|||||||
qDebug() << project->files().toSet() << expectedFiles.toSet();
|
qDebug() << project->files().toSet() << expectedFiles.toSet();
|
||||||
QCOMPARE(project->files().toSet(), expectedFiles.toSet());
|
QCOMPARE(project->files().toSet(), expectedFiles.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// use wildcards
|
||||||
|
//
|
||||||
|
projectFile = QLatin1String(
|
||||||
|
"import QmlProject 1.0\n"
|
||||||
|
"Project {\n"
|
||||||
|
" ImageFiles {\n"
|
||||||
|
" filter: \"?mage.[gf]if\"\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n");
|
||||||
|
|
||||||
|
{
|
||||||
|
QDeclarativeEngine engine;
|
||||||
|
QDeclarativeComponent component(&engine);
|
||||||
|
component.setData(projectFile.toUtf8(), QUrl());
|
||||||
|
if (!component.isReady())
|
||||||
|
qDebug() << component.errorString();
|
||||||
|
QVERIFY(component.isReady());
|
||||||
|
|
||||||
|
QmlProjectItem *project = qobject_cast<QmlProjectItem*>(component.create());
|
||||||
|
QVERIFY(project);
|
||||||
|
|
||||||
|
project->setSourceDirectory(testDataDir);
|
||||||
|
|
||||||
|
QStringList expectedFiles(QStringList() << testDataDir + "/image.gif");
|
||||||
|
qDebug() << project->files().toSet() << expectedFiles.toSet();
|
||||||
|
QCOMPARE(project->files().toSet(), expectedFiles.toSet());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_FileFormat::testMatchesFile()
|
void tst_FileFormat::testMatchesFile()
|
||||||
@@ -269,8 +298,9 @@ void tst_FileFormat::testLibraryPaths()
|
|||||||
|
|
||||||
project->setSourceDirectory(testDataDir);
|
project->setSourceDirectory(testDataDir);
|
||||||
|
|
||||||
QStringList expectedPaths(QStringList() << "../otherLibrary"
|
QStringList expectedPaths(QStringList() << SRCDIR "/otherLibrary"
|
||||||
<< "library");
|
<< SRCDIR "/data/library");
|
||||||
|
qDebug() << expectedPaths << project->importPaths();
|
||||||
QCOMPARE(project->importPaths().toSet(), expectedPaths.toSet());
|
QCOMPARE(project->importPaths().toSet(), expectedPaths.toSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user