Forge
csharp4405de34
1#nullable enable
2using CascadeIDE.Contracts;
3using CascadeIDE.Services;
4
5namespace CascadeIDE.Features.WorkspaceNavigation.Application;
6
7/// <summary>
8/// Выбор якорного .cs для карты намерений, когда в редакторе нет активного файла (Intercom / чат на Forward).
9/// </summary>
10[ComputingUnit]
11public static class WorkspaceNavigationMapAnchorResolver
12{
13 private static readonly string[] PreferredEntryFileNames =
14 [
15 "Program.cs",
16 "App.axaml.cs",
17 "Startup.cs",
18 "MainWindow.axaml.cs"
19 ];
20
21 public static string? Resolve(
22 string? currentPath,
23 IReadOnlyList<string> openDocumentPaths,
24 IReadOnlyList<string> solutionFilePaths)
25 {
26 var known = BuildKnownCsSet(solutionFilePaths);
27 if (known.Count == 0)
28 return null;
29
30 if (TryResolveKnownCs(currentPath, known, out var fromCurrent))
31 return fromCurrent;
32
33 foreach (var open in openDocumentPaths)
34 {
35 if (TryResolveKnownCs(open, known, out var fromOpen))
36 return fromOpen;
37 }
38
39 var csFiles = known.OrderBy(static p => p, StringComparer.OrdinalIgnoreCase).ToList();
40 return PreferEntryPoint(csFiles) ?? csFiles[0];
41 }
42
43 private static HashSet<string> BuildKnownCsSet(IReadOnlyList<string> solutionFilePaths)
44 {
45 var known = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
46 foreach (var path in solutionFilePaths)
47 {
48 if (string.IsNullOrWhiteSpace(path))
49 continue;
50 if (!path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase))
51 continue;
52 if (McpSolutionTree.IsBuildArtifactPath(path))
53 continue;
54
55 try
56 {
57 known.Add(CanonicalFilePath.Normalize(path));
58 }
59 catch
60 {
61 // skip invalid paths from tree
62 }
63 }
64
65 return known;
66 }
67
68 private static bool TryResolveKnownCs(string? path, HashSet<string> known, out string? resolved)
69 {
70 resolved = null;
71 if (string.IsNullOrWhiteSpace(path))
72 return false;
73 if (!path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase))
74 return false;
75
76 try
77 {
78 var normalized = CanonicalFilePath.Normalize(path);
79 if (!known.Contains(normalized))
80 return false;
81 resolved = normalized;
82 return true;
83 }
84 catch
85 {
86 return false;
87 }
88 }
89
90 private static string? PreferEntryPoint(IReadOnlyList<string> csFiles)
91 {
92 foreach (var name in PreferredEntryFileNames)
93 {
94 var hit = csFiles.FirstOrDefault(p =>
95 string.Equals(Path.GetFileName(p), name, StringComparison.OrdinalIgnoreCase));
96 if (hit is not null)
97 return hit;
98 }
99
100 return null;
101 }
102}
103
View only · write via MCP/CIDE