Forge
csharpdeeb25a2
1using CascadeIDE.Models;
2using CascadeIDE.Services;
3using CascadeIDE.ViewModels;
4using CommunityToolkit.Mvvm.Input;
5
6namespace CascadeIDE.Features.Debug;
7
8/// <summary>Relay: DAP отладка (композитор — <see cref="MainWindowViewModel"/>).</summary>
9public sealed partial class MainWindowDebugSessionViewModel : ViewModelBase
10{
11 private static readonly string[] DebugRelayUiPresentationNames =
12 [
13 nameof(MainWindowViewModel.HasDebugSession),
14 nameof(MainWindowViewModel.IsDebugExecutionPaused),
15 nameof(MainWindowViewModel.IsDebugExecutionRunning),
16 nameof(MainWindowViewModel.IdeHealthDebugText),
17 nameof(MainWindowViewModel.IdeHealthDebugCockpitShort),
18 nameof(MainWindowViewModel.IdeHealthMountPayload),
19 nameof(MainWindowViewModel.PfdIdeHealthMountContext),
20 nameof(MainWindowViewModel.MfdIdeHealthMountContext),
21 ];
22
23 private readonly MainWindowViewModel _host;
24
25 public MainWindowDebugSessionViewModel(MainWindowViewModel host) => _host = host;
26
27 internal void NotifyRelayCommandsChanged()
28 {
29 DebugStartOrContinueCommand.NotifyCanExecuteChanged();
30 DebugAttachCommand.NotifyCanExecuteChanged();
31 DebugStopCommand.NotifyCanExecuteChanged();
32 DebugStepOverCommand.NotifyCanExecuteChanged();
33 DebugStepIntoCommand.NotifyCanExecuteChanged();
34 DebugStepOutCommand.NotifyCanExecuteChanged();
35 foreach (var name in DebugRelayUiPresentationNames)
36 _host.McpNotifyPropertyChanged(name);
37 }
38
39 /// <summary><c>debug_launch</c> без JSON: тот же поток, что F5.</summary>
40 internal async Task<string> DebugLaunchInteractiveAsync()
41 {
42 await DebugStartOrContinueAsync().ConfigureAwait(true);
43 return "OK";
44 }
45
46 [RelayCommand(CanExecute = nameof(CanDebugStartOrContinue))]
47 private async Task DebugStartOrContinueAsync()
48 {
49 var dap = _host.DapDebug;
50 if (dap.HasActiveSession && dap.IsExecutionStopped)
51 {
52 try { await dap.ContinueAsync().ConfigureAwait(false); }
53 catch (Exception ex) { await ShowDebugInfoAsync("Отладка", ex.Message).ConfigureAwait(false); }
54 return;
55 }
56
57 if (dap.HasActiveSession && !dap.IsExecutionStopped)
58 {
59 await ShowDebugInfoAsync("Отладка", "Выполнение не остановлено. Дождись брейкпоинта или останови отладку.").ConfigureAwait(false);
60 return;
61 }
62
63 var ws = _host.GetWorkspacePath();
64 if (string.IsNullOrEmpty(ws))
65 {
66 await ShowDebugInfoAsync("Отладка", "Сначала открой решение — нужен каталог workspace для брейкпоинтов.").ConfigureAwait(false);
67 return;
68 }
69
70 var res = await _host.TryResolveDebugLaunchForF5Async().ConfigureAwait(false);
71 if (res is not { } r)
72 {
73 var target = _host.RequestPickDebugTarget != null ? await _host.RequestPickDebugTarget().ConfigureAwait(false) : null;
74 if (string.IsNullOrEmpty(target))
75 return;
76 r = new DebugLaunchResolution(target, null, null, null, OpenLaunchBrowser: false, LaunchUrl: null);
77 }
78
79 try
80 {
81 _host.IsInstrumentationDockVisible = true;
82 _host.CurrentMfdShellPage = MfdShellPage.DebugStack;
83 _ = await dap.LaunchAsync(
84 ws,
85 r.TargetDllPath,
86 netcoredbgPath: null,
87 r.ProgramArgs,
88 r.Environment,
89 r.WorkingDirectoryRelativeToSolution).ConfigureAwait(false);
90 if (r.OpenLaunchBrowser)
91 KestrelLaunchBrowser.TryOpenAfterLaunch(r.Environment, r.LaunchUrl);
92 }
93 catch (Exception ex)
94 {
95 await ShowDebugInfoAsync("Ошибка запуска отладки", ex.Message).ConfigureAwait(false);
96 }
97 }
98
99 private bool CanDebugStartOrContinue()
100 {
101 if (_host.DapDebug.HasActiveSession)
102 return _host.DapDebug.IsExecutionStopped;
103 return true;
104 }
105
106 [RelayCommand(CanExecute = nameof(CanDebugAttach))]
107 private async Task DebugAttachAsync()
108 {
109 var ws = _host.GetWorkspacePath();
110 if (string.IsNullOrEmpty(ws))
111 {
112 await ShowDebugInfoAsync("Отладка", "Сначала открой решение.").ConfigureAwait(false);
113 return;
114 }
115
116 var pid = _host.RequestAttachProcessId != null ? await _host.RequestAttachProcessId().ConfigureAwait(false) : null;
117 if (pid is null or <= 0)
118 return;
119
120 try
121 {
122 _host.IsInstrumentationDockVisible = true;
123 _host.CurrentMfdShellPage = MfdShellPage.DebugStack;
124 _ = await _host.DapDebug.AttachAsync(ws, pid.Value, targetPath: null, netcoredbgPath: null).ConfigureAwait(false);
125 }
126 catch (Exception ex)
127 {
128 await ShowDebugInfoAsync("Ошибка присоединения", ex.Message).ConfigureAwait(false);
129 }
130 }
131
132 private bool CanDebugAttach() => !_host.DapDebug.HasActiveSession;
133
134 [RelayCommand(CanExecute = nameof(CanDebugStop))]
135 private async Task DebugStopAsync()
136 {
137 try { await _host.DapDebug.StopAsync().ConfigureAwait(false); }
138 catch (Exception ex) { await ShowDebugInfoAsync("Отладка", ex.Message).ConfigureAwait(false); }
139 }
140
141 private bool CanDebugStop() => _host.DapDebug.HasActiveSession;
142
143 [RelayCommand(CanExecute = nameof(CanDebugStep))]
144 private async Task DebugStepOverAsync()
145 {
146 try { _ = await _host.DapDebug.StepOverAsync().ConfigureAwait(false); }
147 catch (Exception ex) { await ShowDebugInfoAsync("Отладка", ex.Message).ConfigureAwait(false); }
148 }
149
150 [RelayCommand(CanExecute = nameof(CanDebugStep))]
151 private async Task DebugStepIntoAsync()
152 {
153 try { _ = await _host.DapDebug.StepIntoAsync().ConfigureAwait(false); }
154 catch (Exception ex) { await ShowDebugInfoAsync("Отладка", ex.Message).ConfigureAwait(false); }
155 }
156
157 [RelayCommand(CanExecute = nameof(CanDebugStep))]
158 private async Task DebugStepOutAsync()
159 {
160 try { _ = await _host.DapDebug.StepOutAsync().ConfigureAwait(false); }
161 catch (Exception ex) { await ShowDebugInfoAsync("Отладка", ex.Message).ConfigureAwait(false); }
162 }
163
164 private bool CanDebugStep() => _host.DapDebug.HasActiveSession && _host.DapDebug.IsExecutionStopped;
165
166 private Task ShowDebugInfoAsync(string title, string message) =>
167 _host.ShowDebugInfoAsync(title, message);
168}
169
View only · write via MCP/CIDE