-
-
Notifications
You must be signed in to change notification settings - Fork 85
Fix hidden folder checking on Unix-based OS #574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -52,7 +52,7 @@ public static List<string> GetFiles(string folder, FileScannerConfig config) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<string> filteredResult = unfilteredResult | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Ignore hidden files and folders | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .Where(filePath => (!config.ExcludeHiddenFiles || !IsHiddenFile(filePath)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && (!config.ExcludeHiddenFolders || !IsInsideHiddenFolder(filePath))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && (!config.ExcludeHiddenFolders || !IsInsideHiddenFolder(filePath, folder))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ToList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return filteredResult; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -64,10 +64,11 @@ private static bool IsHiddenFile(string filePath) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Path.GetFileName(filePath).StartsWith("."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static bool IsInsideHiddenFolder(string filePath) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static bool IsInsideHiddenFolder(string filePath, string rootPath) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // On Unix based operating systems, files and folders that start with a dot are hidden. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string folderPath = Path.GetDirectoryName(filePath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return folderPath.Contains("\\.") || folderPath.Contains("/."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Only check for hidden folders that are children of the root path, not the root path itself or its parents. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string folderPath = Path.GetDirectoryName(filePath).Substring(rootPath.Length); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return folderPath.StartsWith(".") || folderPath.Contains("\\.") || folderPath.Contains("/."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+71
to
+72
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string folderPath = Path.GetDirectoryName(filePath).Substring(rootPath.Length); | |
| return folderPath.StartsWith(".") || folderPath.Contains("\\.") || folderPath.Contains("/."); | |
| string directoryPath = Path.GetDirectoryName(filePath); | |
| if (directoryPath == null) | |
| { | |
| return false; | |
| } | |
| string fullRootPath = Path.GetFullPath(rootPath); | |
| string fullDirectoryPath = Path.GetFullPath(directoryPath); | |
| string relativePath; | |
| try | |
| { | |
| relativePath = Path.GetRelativePath(fullRootPath, fullDirectoryPath); | |
| } | |
| catch | |
| { | |
| // If we cannot compute a relative path, conservatively assume it's not inside a hidden folder under root. | |
| return false; | |
| } | |
| // Exclude the root path itself (".") and any paths that are outside the root (".."). | |
| if (relativePath == "." || | |
| relativePath == ".." || | |
| relativePath.StartsWith(".." + Path.DirectorySeparatorChar) || | |
| relativePath.StartsWith(".." + Path.AltDirectorySeparatorChar)) | |
| { | |
| return false; | |
| } | |
| // Check for hidden folders (segments starting with a dot) in the relative path. | |
| return relativePath.StartsWith(".") | |
| || relativePath.Contains(Path.DirectorySeparatorChar + ".") | |
| || relativePath.Contains(Path.AltDirectorySeparatorChar + "."); |
Copilot
AI
Mar 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hidden-folder detection via Contains(\"/.\") / Contains(\"\\\\.\") is brittle and mixes separator conventions; it’s easier to reason about (and less error-prone) if you split the (relative) path into segments and check segment.StartsWith('.') for each segment. If you keep the current approach, StartsWith(\".\") likely won’t match when the relative path begins with a separator (e.g., \"/.config\"), so consider trimming leading directory separators before applying StartsWith.
| return folderPath.StartsWith(".") || folderPath.Contains("\\.") || folderPath.Contains("/."); | |
| // Normalize the relative folder path and check each segment for a leading dot. | |
| if (string.IsNullOrEmpty(folderPath)) | |
| { | |
| return false; | |
| } | |
| folderPath = folderPath.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); | |
| if (folderPath.Length == 0) | |
| { | |
| return false; | |
| } | |
| string[] segments = folderPath.Split( | |
| new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, | |
| StringSplitOptions.RemoveEmptyEntries); | |
| return segments.Any(segment => segment.StartsWith(".")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for tackling this.
I think this should work fine when rootPath is known to be a prefix of filePath.
But I am not sure whether this is the case when only looking at this code.
Consider to wrap rootPath in
Path.GetFullPath(), to make it obvious that both are absolute paths.Besides, performance is relevant here because there can be thousands of files (some users have +20k UltraStar songs). So only wrap it once, not inside the loop.
Please test performance with lots of dummy files. Ideally, performance should be similar as before. For example, this repo has several thousand txt files that you can use to test performance https://github.com/Asvarox/allkaraoke
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, seems over engineered. Also performance has to be tested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure
rootPathwill always be a prefix offilePath. The Directory.GetFiles documentation says thatI read that as a promise that the path parameter is always a prefix of any returned file paths. A quick test confirms that if you give it
.it will return./aand./b. If you give it/home/user/dirit will return/home/user/dir/aand/home/user/dir/b. If you give it../..it will return../../aand../../b.I agree it's not clear from the code that this is the case, so maybe a comment would clarify it. Performance should be about the same as before as
SubstringandStartsWithare cheap operations compared to filesystem checks. I considered adding an explicit check ifrootPathis a prefix offilePathbefore doingSubstringbut that feels unnecessary as the documentation seems to already promise that.