| 1 | #nullable enable |
| 2 | |
| 3 | using CascadeIDE.Services; |
| 4 | |
| 5 | namespace CascadeIDE.ViewModels; |
| 6 | |
| 7 | /// <summary>MCP: постановка брейкпоинта с загрузкой решения и показом строки в редакторе.</summary> |
| 8 | public partial class MainWindowViewModel |
| 9 | { |
| 10 | /// <summary>Полный сценарий для MCP <c>set_breakpoint</c>: при необходимости загрузка решения, регистрация точки, открытие файла и переход к строке.</summary> |
| 11 | internal async Task<string> CompleteMcpSetBreakpointAsync(string filePath, int line, string? condition, CancellationToken cancellationToken) |
| 12 | { |
| 13 | cancellationToken.ThrowIfCancellationRequested(); |
| 14 | if (string.IsNullOrEmpty(filePath) || line < 1) |
| 15 | return "Missing file_path or line"; |
| 16 | string path; |
| 17 | try |
| 18 | { |
| 19 | path = CanonicalFilePath.Normalize(filePath); |
| 20 | } |
| 21 | catch |
| 22 | { |
| 23 | return "Invalid file_path"; |
| 24 | } |
| 25 | |
| 26 | if (!File.Exists(path)) |
| 27 | return "File not found"; |
| 28 | |
| 29 | var sln = SolutionFileLocator.TryFindSolutionForSourceFile(path); |
| 30 | if (SolutionFileLocator.NeedsLoadSolutionBeforeBreakpoint(sln, Workspace.SolutionPath)) |
| 31 | { |
| 32 | cancellationToken.ThrowIfCancellationRequested(); |
| 33 | await LoadSolutionAsync(sln!).ConfigureAwait(false); |
| 34 | } |
| 35 | |
| 36 | await UiScheduler.Default.InvokeAsync(() => |
| 37 | { |
| 38 | Editor.RegisterIdeMcpBreakpoint(path, line, condition); |
| 39 | IdeMcp.GoToPosition(path, line, 1, line, 1); |
| 40 | _focusEditorAction?.Invoke(); |
| 41 | }).ConfigureAwait(false); |
| 42 | |
| 43 | return "OK"; |
| 44 | } |
| 45 | |
| 46 | internal async void ResyncDapBreakpointsFireAndForget() |
| 47 | { |
| 48 | try |
| 49 | { |
| 50 | await DapDebug.ResyncBreakpointsFromStorageAsync().ConfigureAwait(false); |
| 51 | } |
| 52 | catch |
| 53 | { |
| 54 | // best-effort: нет сессии или DAP занят |
| 55 | } |
| 56 | } |
| 57 | } |
| 58 | |