Forge
csharp4405de34
1#nullable enable
2
3using System.Collections.ObjectModel;
4using CommunityToolkit.Mvvm.ComponentModel;
5using CascadeIDE.Features.Workspace;
6using CascadeIDE.Features.Workspace.DataAcquisition;
7using CascadeIDE.Features.Forge.Lens;
8using CascadeIDE.Features.WorkspaceNavigation.Application;
9using CascadeIDE.Models;
10using CascadeIDE.Services;
11using CommunityToolkit.Mvvm.Input;
12
13namespace CascadeIDE.Features.WorkspaceNavigation.Presentation;
14
15public sealed partial class WorkspaceNavigationMapViewModel
16{
17 public ObservableCollection<WorkspaceCorrespondenceDocItemVm> WorkspaceCorrespondenceDocItems { get; } = new();
18
19 public ObservableCollection<WorkspaceReverseAnchorItemVm> WorkspaceReverseAnchorItems { get; } = new();
20
21 [ObservableProperty]
22 private string _workspaceReverseAnchorsStatus = "";
23
24 [ObservableProperty]
25 private string[] _workspaceCorrespondenceDocPaths = [];
26
27 /// <summary>Канон: развернуть Mfd, страница CRS, refresh якоря (см. <c>show_correspondence_page</c>).</summary>
28 [RelayCommand]
29 private void ShowCorrespondencePage()
30 {
31 _host.ApplyMfdRegionExpanded(true);
32 _host.TryNavigateToMfdShellPage(MfdShellPage.Correspondence);
33 ScheduleWorkspaceNavigationMapRefresh();
34 }
35
36 [RelayCommand]
37 private void OpenCorrespondenceDocument(object? parameter)
38 {
39 string? docPath;
40 int? lineHint = null;
41
42 if (parameter is WorkspaceReverseAnchorItemVm reverse)
43 {
44 docPath = reverse.DocPath;
45 lineHint = reverse.DocLineHint;
46 }
47 else
48 {
49 docPath = parameter as string;
50 }
51
52 if (string.IsNullOrWhiteSpace(docPath))
53 return;
54
55 OpenRepoDocumentInMarkdownPreview(docPath, lineHint);
56 }
57
58 private void ApplyCorrespondenceDocAndReverseLists(
59 string? workspaceRoot,
60 string? navigationPath,
61 string[] adrDocRepoPaths)
62 {
63 WorkspaceCorrespondenceDocPaths = adrDocRepoPaths;
64
65 WorkspaceCorrespondenceDocItems.Clear();
66 foreach (var p in adrDocRepoPaths)
67 {
68 WorkspaceCorrespondenceDocItems.Add(new WorkspaceCorrespondenceDocItemVm
69 {
70 DocPath = p,
71 DisplayTitle = WorkspaceAdrMapResolver.GuessAdrPreviewTitle(p)
72 });
73 }
74
75 WorkspaceReverseAnchorItems.Clear();
76 var workspaceToml = RepositoryWorkspaceTomlLoader.TryLoad(workspaceRoot);
77 if (string.IsNullOrWhiteSpace(workspaceRoot) || string.IsNullOrWhiteSpace(navigationPath))
78 {
79 WorkspaceReverseAnchorsStatus = adrDocRepoPaths.Length == 0
80 ? ""
81 : "Reverse anchors: укажите якорь файла в редакторе.";
82 return;
83 }
84
85 var explicitAnchors = WorkspaceCorrespondenceCodeAnchorsLoader.LoadFromWorkspaceToml(workspaceToml, workspaceRoot);
86 var reverse = DocReverseAnchorResolver.Resolve(workspaceRoot, navigationPath, adrDocRepoPaths, explicitAnchors);
87 foreach (var m in reverse)
88 {
89 WorkspaceReverseAnchorItems.Add(new WorkspaceReverseAnchorItemVm
90 {
91 DocPath = m.DocPath,
92 DisplayTitle = m.DocTitle,
93 Excerpt = m.Excerpt,
94 Provenance = m.Provenance,
95 DocLineHint = m.DocLineHint
96 });
97 }
98
99 var anchorRel = WorkspaceAdrMapResolver.TryComputeRepoRelativePath(workspaceRoot, navigationPath);
100 if (!string.IsNullOrWhiteSpace(anchorRel))
101 {
102 foreach (var forgeItem in ForgeLensCorrespondenceClient.TryLoadForFile(workspaceToml, workspaceRoot, anchorRel))
103 {
104 WorkspaceReverseAnchorItems.Add(new WorkspaceReverseAnchorItemVm
105 {
106 DocPath = forgeItem.Url,
107 DisplayTitle = forgeItem.DisplayTitle,
108 Excerpt = forgeItem.Status,
109 Provenance = ForgeLensCorrespondenceClient.Provenance,
110 });
111 }
112 }
113
114 var forgeCount = WorkspaceReverseAnchorItems.Count - reverse.Count;
115 WorkspaceReverseAnchorsStatus = reverse.Count == 0 && forgeCount == 0
116 ? (adrDocRepoPaths.Length > 0
117 ? "Reverse anchors: в связанных ADR явных ссылок на этот файл не найдено (bracket / scan)."
118 : "")
119 : $"Reverse anchors: {reverse.Count}" + (forgeCount > 0 ? $", forge: {forgeCount}" : "");
120 }
121
122 private void OpenRepoDocumentInMarkdownPreview(string docPath, int? scrollToLine = null)
123 {
124 var wsRoot = _host.GetWorkspacePath();
125 if (string.IsNullOrWhiteSpace(wsRoot))
126 return;
127
128 if (!WorkspaceMarkdownPreviewOpener.TryOpenRepoDocument(
129 wsRoot,
130 docPath,
131 (title, content, source) => _host.MarkdownPreviewTool.SetContent(title, content, source, scrollToLine),
132 out _))
133 return;
134
135 _host.ApplyMfdRegionExpanded(true);
136 _host.TryNavigateToMfdShellPage(MfdShellPage.MarkdownPreview);
137 }
138}
139
View only · write via MCP/CIDE