Forge
csharpdeeb25a2
1using System.Text.RegularExpressions;
2using CascadeIDE.Models;
3using CascadeIDE.Services;
4
5namespace CascadeIDE.Features.Agent.Environment;
6
7/// <summary>Узкий <c>dotnet test --filter</c> по затронутым типам (<see cref="VerifyRung.TestScoped"/>, ADR 0148 W6/F).</summary>
8public static class AgentTestScopedTouchedTestFilter
9{
10 private static readonly Regex s_typeName = new(
11 @"\b(?:class|record|struct|interface)\s+(\w+)",
12 RegexOptions.Compiled);
13
14 public static async Task<string?> BuildFilterExpressionAsync(
15 AgentEnvironmentLadderSettings ladder,
16 IGitCommandRunner? git,
17 string? workspaceRoot,
18 CancellationToken cancellationToken = default)
19 {
20 if (!ladder.TestScopedTouchedTestsOnly)
21 return null;
22
23 if (git is null || string.IsNullOrWhiteSpace(workspaceRoot) || !Directory.Exists(workspaceRoot))
24 return null;
25
26 var paths = await AgentGitDirtyCsPaths.CollectAsync(
27 git,
28 workspaceRoot,
29 ladder.DiagnoseFilesGitDirtyMaxFiles,
30 cancellationToken).ConfigureAwait(false);
31
32 if (paths.Count == 0)
33 return null;
34
35 var names = new HashSet<string>(StringComparer.Ordinal);
36 foreach (var path in paths)
37 {
38 cancellationToken.ThrowIfCancellationRequested();
39 string text;
40 try
41 {
42 text = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false);
43 }
44 catch
45 {
46 continue;
47 }
48
49 foreach (Match m in s_typeName.Matches(text))
50 names.Add(m.Groups[1].Value);
51 }
52
53 if (names.Count == 0)
54 return null;
55
56 return string.Join("|", names.Select(n => $"FullyQualifiedName~{n}"));
57 }
58}
59
View only · write via MCP/CIDE