ProjectExplorer: allow to avoid scanning workspace subdirectories

In order to provide file watchers for the workspace project we need a
way to completely exclude subdirectory of a workspace project. Otherwise
we might end up with an unnecessary amount of notifications for quickly
changing subdirectories like a build dir inside the workspace project.

Use the previously available "files.exclude" and fully remove the
entries of the project tree and avoid the scanning of the folders in
that list.

Change-Id: If11f62b1eabb4a7f03e4445e5f37068466feda4d
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
David Schulz
2024-09-06 14:39:13 +02:00
parent 3840f31b4a
commit 9bcbaca9a1
3 changed files with 27 additions and 32 deletions

View File

@@ -54,7 +54,7 @@ bool TreeScanner::asyncScanForFiles(const Utils::FilePath &directory)
return true;
}
void TreeScanner::setFilter(TreeScanner::FileFilter filter)
void TreeScanner::setFilter(TreeScanner::Filter filter)
{
if (isFinished())
m_filter = filter;
@@ -182,10 +182,17 @@ static DirectoryScanResult scanForFilesImpl(
return result;
}
static const Utils::MimeType &directoryMimeType()
{
static const Utils::MimeType mimeType = Utils::mimeTypeForName("inode/directory");
return mimeType;
}
static QList<FileNode *> scanForFilesHelper(
TreeScanner::Promise &promise,
const Utils::FilePath &directory,
QDir::Filters filter,
QDir::Filters dirfilter,
const TreeScanner::Filter &filter,
const std::function<FileNode *(const Utils::FilePath &)> &factory)
{
const QFuture<void> future(promise.future());
@@ -195,7 +202,7 @@ static QList<FileNode *> scanForFilesHelper(
promise.setProgressRange(0, progressRange);
QSet<Utils::FilePath> visited;
const DirectoryScanResult result = scanForFilesImpl(future, directory, filter, factory, versionControls);
const DirectoryScanResult result = scanForFilesImpl(future, directory, dirfilter, factory, versionControls);
QList<FileNode *> fileNodes = result.nodes;
const int progressIncrement = int(
progressRange / static_cast<double>(fileNodes.count() + result.subDirectories.count()));
@@ -203,11 +210,13 @@ static QList<FileNode *> scanForFilesHelper(
QList<QPair<Utils::FilePath, int>> subDirectories;
auto addSubDirectories = [&](const Utils::FilePaths &subdirs, int progressIncrement) {
for (const Utils::FilePath &subdir : subdirs) {
if (Utils::insert(visited, subdir.canonicalPath()))
if (Utils::insert(visited, subdir.canonicalPath())
&& !(filter && filter(directoryMimeType(), subdir))) {
subDirectories.append(qMakePair(subdir, progressIncrement));
else
} else {
promise.setProgressValue(future.progressValue() + progressIncrement);
}
}
};
addSubDirectories(result.subDirectories, progressIncrement);
@@ -218,7 +227,7 @@ static QList<FileNode *> scanForFilesHelper(
auto onSetup = [&, iterator](Utils::Async<DirectoryScanResult> &task) {
task.setConcurrentCallData(
scanForFilesImpl, future, iterator->first, filter, factory, versionControls);
scanForFilesImpl, future, iterator->first, dirfilter, factory, versionControls);
};
auto onDone = [&, iterator](const Utils::Async<DirectoryScanResult> &task) {
@@ -250,12 +259,12 @@ static QList<FileNode *> scanForFilesHelper(
void TreeScanner::scanForFiles(
Promise &promise,
const Utils::FilePath &directory,
const FileFilter &filter,
const Filter &filter,
QDir::Filters dirFilter,
const FileTypeFactory &factory)
{
QList<FileNode *> nodes = scanForFilesHelper(
promise, directory, dirFilter, [&filter, &factory](const Utils::FilePath &fn) -> FileNode * {
promise, directory, dirFilter, filter, [&filter, &factory](const Utils::FilePath &fn) -> FileNode * {
const Utils::MimeType mimeType = Utils::mimeTypesForFileName(fn.path()).value(0);
// Skip some files during scan.

View File

@@ -33,7 +33,7 @@ public:
using FutureWatcher = QFutureWatcher<Result>;
using Promise = QPromise<Result>;
using FileFilter = std::function<bool(const Utils::MimeType &, const Utils::FilePath &)>;
using Filter = std::function<bool(const Utils::MimeType &, const Utils::FilePath &)>;
using FileTypeFactory = std::function<ProjectExplorer::FileType(const Utils::MimeType &, const Utils::FilePath &)>;
explicit TreeScanner(QObject *parent = nullptr);
@@ -43,7 +43,7 @@ public:
bool asyncScanForFiles(const Utils::FilePath& directory);
// Setup filter for ignored files
void setFilter(FileFilter filter);
void setFilter(Filter filter);
// Setup dir filters for scanned folders
void setDirFilter(QDir::Filters dirFilter);
@@ -74,12 +74,12 @@ signals:
private:
static void scanForFiles(Promise &fi,
const Utils::FilePath &directory,
const FileFilter &filter,
const Filter &filter,
QDir::Filters dirFilter,
const FileTypeFactory &factory);
private:
FileFilter m_filter;
Filter m_filter;
QDir::Filters m_dirFilter = QDir::AllEntries | QDir::NoDotAndDotDot;
FileTypeFactory m_factory;

View File

@@ -49,18 +49,6 @@ const expected_str<QJsonObject> projectDefinition(const Project *project)
return {};
}
static bool checkEnabled(FolderNode *fn)
{
if (fn->findChildFileNode([](FileNode *fn) { return fn->isEnabled(); }))
return true;
if (fn->findChildFolderNode([](FolderNode *fn) { return checkEnabled(fn); }))
return true;
fn->setEnabled(false);
return false;
}
class WorkspaceBuildSystem final : public BuildSystem
{
public:
@@ -71,15 +59,10 @@ public:
auto root = std::make_unique<ProjectNode>(projectDirectory());
root->setDisplayName(target()->project()->displayName());
std::vector<std::unique_ptr<FileNode>> nodePtrs
= Utils::transform<std::vector>(m_scanner.release().allFiles, [this](FileNode *fn) {
fn->setEnabled(!Utils::anyOf(
m_filters, [path = fn->path().path()](const QRegularExpression &filter) {
return filter.match(path).hasMatch();
}));
= Utils::transform<std::vector>(m_scanner.release().allFiles, [](FileNode *fn) {
return std::unique_ptr<FileNode>(fn);
});
root->addNestedNodes(std::move(nodePtrs));
root->forEachFolderNode(&checkEnabled);
setRootProjectNode(std::move(root));
m_parseGuard.markAsSuccess();
@@ -88,6 +71,11 @@ public:
emitBuildSystemUpdated();
});
m_scanner.setDirFilter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
m_scanner.setFilter([&](const Utils::MimeType &, const Utils::FilePath &filePath) {
return Utils::anyOf(m_filters, [filePath](const QRegularExpression &filter) {
return filter.match(filePath.path()).hasMatch();
});
});
connect(target()->project(),
&Project::projectFileIsDirty,
@@ -110,8 +98,6 @@ public:
for (const QJsonValue &excludeJson : excludesJson) {
if (excludeJson.isString()) {
FilePath absolute = projectPath.pathAppended(excludeJson.toString());
if (absolute.isDir())
absolute = absolute.pathAppended("*");
m_filters << QRegularExpression(
Utils::wildcardToRegularExpression(absolute.path()),
QRegularExpression::CaseInsensitiveOption);