From 72e9b678e44327cfc756b45a5bbe65888fb70fcc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:51:29 +0000 Subject: [PATCH 1/3] Initial plan From b97ba563b4b3efb02410663a318f67a7dddd0b72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:54:43 +0000 Subject: [PATCH 2/3] refactor: optimize directory traversal with manual recursion Co-authored-by: HandyS11 <62420910+HandyS11@users.noreply.github.com> --- .../ClassAnalysis/WorkspaceTypeDiscovery.cs | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/ProjGraph.Lib/Services/ClassAnalysis/WorkspaceTypeDiscovery.cs b/src/ProjGraph.Lib/Services/ClassAnalysis/WorkspaceTypeDiscovery.cs index a386caa..03dd0f1 100644 --- a/src/ProjGraph.Lib/Services/ClassAnalysis/WorkspaceTypeDiscovery.cs +++ b/src/ProjGraph.Lib/Services/ClassAnalysis/WorkspaceTypeDiscovery.cs @@ -58,20 +58,28 @@ 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)) - { - 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; - } + /// + /// 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) + { + // Directories to skip during recursion for better performance + var excludedDirs = new HashSet(StringComparer.OrdinalIgnoreCase) { "bin", "obj", ".git", "node_modules" }; + // Search files in the current directory + foreach (var file in Directory.EnumerateFiles(directory, "*.cs", new EnumerationOptions { IgnoreInaccessible = true })) + { // 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 +98,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 (excludedDirs.Contains(dirName)) + { + continue; + } + + var result = await SearchDirectoryRecursiveAsync(subDir, typeName); + if (result != null) + { + return result; } } From 73e31ab68cbb50274a27195455ea0c817dbb64b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:55:55 +0000 Subject: [PATCH 3/3] perf: use static field for excluded directories to avoid repeated allocations Co-authored-by: HandyS11 <62420910+HandyS11@users.noreply.github.com> --- .../Services/ClassAnalysis/WorkspaceTypeDiscovery.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ProjGraph.Lib/Services/ClassAnalysis/WorkspaceTypeDiscovery.cs b/src/ProjGraph.Lib/Services/ClassAnalysis/WorkspaceTypeDiscovery.cs index 03dd0f1..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, @@ -72,9 +79,6 @@ public static class WorkspaceTypeDiscovery /// private static async Task SearchDirectoryRecursiveAsync(string directory, string typeName) { - // Directories to skip during recursion for better performance - var excludedDirs = new HashSet(StringComparer.OrdinalIgnoreCase) { "bin", "obj", ".git", "node_modules" }; - // Search files in the current directory foreach (var file in Directory.EnumerateFiles(directory, "*.cs", new EnumerationOptions { IgnoreInaccessible = true })) { @@ -106,7 +110,7 @@ public static class WorkspaceTypeDiscovery foreach (var subDir in Directory.EnumerateDirectories(directory, "*", new EnumerationOptions { IgnoreInaccessible = true })) { var dirName = Path.GetFileName(subDir); - if (excludedDirs.Contains(dirName)) + if (ExcludedDirectories.Contains(dirName)) { continue; }