Skip to content

Commit d620b0d

Browse files
committed
Rewrite directory scanner to better handle ignored directories
1 parent dcb21dc commit d620b0d

File tree

2 files changed

+58
-59
lines changed

2 files changed

+58
-59
lines changed

loc/DirectoryScanner.cpp

Lines changed: 57 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -12,54 +12,52 @@ namespace fs = std::filesystem;
1212
std::vector<std::string> DirectoryScanner::Scan(const fs::path& directory, const std::vector<std::string>& extensions,
1313
const std::vector<std::string>& ignoreDirs) const
1414
{
15-
// Scan a directory and return a list of all the paths to the files with the given extensions
16-
std::vector<std::string> file_paths;
17-
18-
if (fs::exists(directory) && fs::is_directory(directory))
19-
{
20-
for (const auto& entry : fs::recursive_directory_iterator(directory))
21-
{
22-
if (fs::is_regular_file(entry.status()))
23-
{
24-
// Check if the file has one of the extensions
25-
bool has_extension = false;
26-
for (const auto& extension : extensions)
27-
{
28-
if (entry.path().extension() == extension)
29-
{
30-
has_extension = true;
31-
break;
32-
}
33-
}
34-
if (!has_extension)
35-
continue;
36-
37-
try
38-
{
39-
file_paths.push_back(entry.path().string());
40-
}
41-
catch(const std::system_error& e)
42-
{
43-
// Sometimes throws this error:
44-
// No mapping for the Unicode character exists in the target multi-byte code page
15+
std::vector<std::string> result;
16+
17+
if (!fs::exists(directory) || !fs::is_directory(directory)) {
18+
return result;
19+
}
20+
21+
// Normalize ignore directories into absolute canonical paths
22+
std::vector<fs::path> ignorePaths;
23+
for (const auto& dir : ignoreDirs) {
24+
fs::path p(dir);
25+
if (p.is_absolute()) {
26+
ignorePaths.push_back(fs::weakly_canonical(p));
27+
} else {
28+
ignorePaths.push_back(fs::weakly_canonical(directory / p));
29+
}
30+
}
4531

46-
// Ignore the file and print a warning
47-
std::cerr << "Warning: System error thrown while getting a file path. Skipping file.\n";
48-
std::cerr << "Error message: " << e.what() << '\n';
32+
fs::recursive_directory_iterator it(directory), end;
33+
while (it != end) {
34+
const fs::directory_entry& entry = *it;
4935

50-
continue;
51-
}
52-
}
36+
if (entry.is_directory()) {
37+
fs::path current = fs::weakly_canonical(entry.path());
38+
39+
// Skip if inside any ignored directory
40+
bool skip = std::any_of(ignorePaths.begin(), ignorePaths.end(),
41+
[&](const fs::path& ignore) {
42+
return isSubPath(ignore, current);
43+
});
44+
45+
if (skip) {
46+
it.disable_recursion_pending(); // skip this whole subtree
47+
}
5348
}
49+
else if (entry.is_regular_file()) {
50+
std::string ext = entry.path().extension().string();
51+
52+
if (std::find(extensions.begin(), extensions.end(), ext) != extensions.end()) {
53+
result.push_back(entry.path().string());
54+
}
55+
}
56+
57+
++it;
5458
}
55-
else
56-
{
57-
std::cerr << "Directory does not exist or is not a directory " << directory << std::endl;
58-
}
59-
60-
// Remove files in ignored directories
61-
RemoveIgnoredFiles(file_paths, ignoreDirs);
62-
return file_paths;
59+
60+
return result;
6361
}
6462

6563
std::vector<std::string> DirectoryScanner::FindIgnoredDirectories(const std::filesystem::path &directory, const std::vector<std::string> &ignorePatterns) const
@@ -102,19 +100,20 @@ std::vector<std::string> DirectoryScanner::FindIgnoredDirectories(const std::fil
102100
return ignoredDirectories;
103101
}
104102

105-
void DirectoryScanner::RemoveIgnoredFiles(std::vector<std::string> &filePaths, const std::vector<std::string> &ignoreDirs) const
103+
bool DirectoryScanner::isSubPath(const std::filesystem::path &base, const std::filesystem::path &path) const
106104
{
107-
filePaths.erase(std::remove_if(filePaths.begin(), filePaths.end(),
108-
[&ignoreDirs](const std::string& filePath)
109-
{
110-
for (const auto& ignoreDir : ignoreDirs)
111-
{
112-
if (filePath.find(ignoreDir) != std::string::npos)
113-
{
114-
return true; // Remove this file
115-
}
116-
}
117-
return false; // Keep this file
118-
}),
119-
filePaths.end());
105+
auto baseAbs = fs::weakly_canonical(base);
106+
auto pathAbs = fs::weakly_canonical(path);
107+
108+
auto baseIt = baseAbs.begin();
109+
auto pathIt = pathAbs.begin();
110+
111+
for (; baseIt != baseAbs.end() && pathIt != pathAbs.end(); ++baseIt, ++pathIt) {
112+
if (*baseIt != *pathIt) {
113+
return false;
114+
}
115+
}
116+
117+
// True if base was fully consumed (base == path, or base is a prefix of path)
118+
return baseIt == baseAbs.end();
120119
}

loc/include/DirectoryScanner.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ class DirectoryScanner
1616
std::vector<std::string> FindIgnoredDirectories(const std::filesystem::path& directory, const std::vector<std::string>& ignorePatterns) const;
1717

1818
private:
19-
void RemoveIgnoredFiles(std::vector<std::string>& filePaths, const std::vector<std::string>& ignoreDirs) const;
19+
bool isSubPath(const std::filesystem::path& base, const std::filesystem::path& path) const;
2020
};

0 commit comments

Comments
 (0)