diff --git a/src/ProjGraph.Lib/Services/ClassAnalysis/WorkspaceTypeDiscovery.cs b/src/ProjGraph.Lib/Services/ClassAnalysis/WorkspaceTypeDiscovery.cs
index a386caa..f4480dd 100644
--- a/src/ProjGraph.Lib/Services/ClassAnalysis/WorkspaceTypeDiscovery.cs
+++ b/src/ProjGraph.Lib/Services/ClassAnalysis/WorkspaceTypeDiscovery.cs
@@ -10,6 +10,13 @@ namespace ProjGraph.Lib.Services.ClassAnalysis;
///
public static class WorkspaceTypeDiscovery
{
+ ///
+ /// Directories to skip during recursive file search for better performance.
+ ///
+ private static readonly HashSet ExcludedDirectories = new(StringComparer.OrdinalIgnoreCase)
+ {
+ "bin", "obj", ".git", "node_modules"
+ };
///
/// Finds the file containing the definition of a specific type within a given directory or its subdirectories.
/// The method first attempts to search in common subdirectories for better performance, and if not found,
@@ -58,20 +65,25 @@ public static class WorkspaceTypeDiscovery
///
private static async Task SearchDirectoryForTypeAsync(string directory, string typeName)
{
- var options = new EnumerationOptions { RecurseSubdirectories = true, IgnoreInaccessible = true };
+ return await SearchDirectoryRecursiveAsync(directory, typeName);
+ }
- foreach (var file in Directory.EnumerateFiles(directory, "*.cs", options))
+ ///
+ /// Recursively searches a directory and its subdirectories for a C# file containing a specific type definition.
+ /// This method manually handles recursion to avoid descending into common non-source directories for better performance.
+ ///
+ /// The path of the directory to search.
+ /// The name of the type to search for.
+ ///
+ /// The full path of the file containing the type definition if found; otherwise, null.
+ ///
+ private static async Task SearchDirectoryRecursiveAsync(string directory, string typeName)
+ {
+ // Search files in the current directory
+ foreach (var file in Directory.EnumerateFiles(directory, "*.cs", new EnumerationOptions { IgnoreInaccessible = true }))
{
- var fullPath = Path.GetFullPath(file);
- // Skip common non-source directories
- var pathSegments = fullPath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
- if (pathSegments.Any(s => s is "bin" or "obj" or ".git" or "node_modules"))
- {
- continue;
- }
-
// Simple string check first for performance
- var content = await File.ReadAllTextAsync(fullPath);
+ var content = await File.ReadAllTextAsync(file);
if (!content.Contains($"class {typeName}") &&
!content.Contains($"interface {typeName}") &&
!content.Contains($"struct {typeName}") &&
@@ -90,7 +102,23 @@ public static class WorkspaceTypeDiscovery
if (hasType)
{
- return fullPath;
+ return file;
+ }
+ }
+
+ // Recursively search subdirectories, skipping excluded directories
+ foreach (var subDir in Directory.EnumerateDirectories(directory, "*", new EnumerationOptions { IgnoreInaccessible = true }))
+ {
+ var dirName = Path.GetFileName(subDir);
+ if (ExcludedDirectories.Contains(dirName))
+ {
+ continue;
+ }
+
+ var result = await SearchDirectoryRecursiveAsync(subDir, typeName);
+ if (result != null)
+ {
+ return result;
}
}