Forge
csharpdeeb25a2
1using System.IO;
2using Avalonia.Threading;
3using CascadeIDE.Features.Workspace.Application;
4using CascadeIDE.Services;
5using CommunityToolkit.Mvvm.ComponentModel;
6
7namespace CascadeIDE.Features.Editor;
8
9/// <summary>Глифы брейкпоинтов и подсветка текущей строки DAP в редакторе.</summary>
10public sealed partial class EditorWorkspaceViewModel
11{
12 private static readonly string[] BreakpointGlyphBindingNames =
13 [
14 nameof(BreakpointLinesInCurrentFile),
15 nameof(AllBreakpointLinesInCurrentFile),
16 nameof(DebugCurrentLineInCurrentFile),
17 ];
18
19 private FileSystemWatcher? _breakpointsFileWatcher;
20
21 public IReadOnlyList<int> BreakpointLinesInCurrentFile => AllBreakpointLinesInCurrentFile;
22
23 public IReadOnlyList<int> GetAllBreakpointLinesForFile(string? filePath)
24 {
25 if (string.IsNullOrEmpty(filePath))
26 return [];
27 var normalized = CanonicalFilePath.Normalize(filePath);
28 return _host.DapDebug.GetSnapshot().Breakpoints
29 .Where(b => CanonicalFilePath.EqualsNormalized(normalized, b.File))
30 .Select(b => b.Line)
31 .OrderBy(static line => line)
32 .Distinct()
33 .ToList();
34 }
35
36 public IReadOnlyList<int> AllBreakpointLinesInCurrentFile =>
37 GetAllBreakpointLinesForFile(CurrentFilePath);
38
39 [ObservableProperty]
40 [NotifyPropertyChangedFor(nameof(DebugCurrentLineInCurrentFile))]
41 private string? _debugPositionFile;
42
43 [ObservableProperty]
44 [NotifyPropertyChangedFor(nameof(DebugCurrentLineInCurrentFile))]
45 private int _debugPositionLine;
46
47 public int GetDebugCurrentLineForFile(string? filePath)
48 {
49 if (string.IsNullOrEmpty(DebugPositionFile) || string.IsNullOrEmpty(filePath))
50 return 0;
51 if (!CanonicalFilePath.Equals(DebugPositionFile, filePath))
52 return 0;
53 return DebugPositionLine;
54 }
55
56 public int DebugCurrentLineInCurrentFile => GetDebugCurrentLineForFile(CurrentFilePath);
57
58 internal void NotifyBreakpointGlyphBindings()
59 {
60 foreach (var name in BreakpointGlyphBindingNames)
61 OnPropertyChanged(name);
62 }
63
64 internal void AttachBreakpointsFileWatcher(string? solutionPath)
65 {
66 _breakpointsFileWatcher?.Dispose();
67 _breakpointsFileWatcher = null;
68 var ws = WorkspaceDirectoryFromSolutionPath.Resolve(solutionPath);
69 _host.DapDebug.RefreshBreakpointSnapshotFromStorage(ws);
70 if (string.IsNullOrEmpty(ws) || !Directory.Exists(ws))
71 return;
72 try
73 {
74 _breakpointsFileWatcher = new FileSystemWatcher(ws)
75 {
76 Filter = BreakpointsFileService.FileName,
77 NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName
78 };
79 _breakpointsFileWatcher.Changed += (_, _) => UiScheduler.Default.Post(() =>
80 {
81 RefreshBreakpointSnapshotFromWorkspace(solutionPath);
82 NotifyBreakpointGlyphBindings();
83 });
84 _breakpointsFileWatcher.Renamed += (_, _) => UiScheduler.Default.Post(() =>
85 {
86 RefreshBreakpointSnapshotFromWorkspace(solutionPath);
87 NotifyBreakpointGlyphBindings();
88 });
89 _breakpointsFileWatcher.EnableRaisingEvents = true;
90 }
91 catch
92 {
93 }
94 }
95
96 private void RefreshBreakpointSnapshotFromWorkspace(string? solutionPath)
97 {
98 var ws = WorkspaceDirectoryFromSolutionPath.Resolve(solutionPath);
99 _host.DapDebug.RefreshBreakpointSnapshotFromStorage(ws);
100 }
101
102 internal void RegisterIdeMcpBreakpoint(string filePath, int line, string? condition)
103 {
104 if (string.IsNullOrEmpty(filePath) || line < 1)
105 return;
106 var path = CanonicalFilePath.Normalize(filePath);
107 var ws = _host.GetWorkspacePath();
108 if (!string.IsNullOrEmpty(ws))
109 BreakpointsFileService.SetBreakpointForBundledSampleTarget(ws, path, line, condition);
110 NotifyBreakpointGlyphBindings();
111 _host.ResyncDapBreakpointsFireAndForget();
112 }
113}
114
View only · write via MCP/CIDE