| 1 | using System.Collections.ObjectModel; |
| 2 | using CascadeIDE.Cockpit.Channels.WorkspaceHealth; |
| 3 | using CascadeIDE.Cockpit.Composition.HostSurface; |
| 4 | using CascadeIDE.Cockpit.Composition.WorkspaceHealth; |
| 5 | using CascadeIDE.Cockpit.ComputingUnits.IdeHealth; |
| 6 | using CascadeIDE.Cockpit.DataBus; |
| 7 | |
| 8 | namespace CascadeIDE.ViewModels; |
| 9 | |
| 10 | /// <summary>Связка с Workspace Health.</summary> |
| 11 | public partial class MainWindowViewModel |
| 12 | { |
| 13 | /// <summary>Свойства, читающие кэш после <see cref="RebuildIdeHealth"/>; держать в одном месте с циклом notify.</summary> |
| 14 | private static readonly string[] IdeHealthRebuildPresentationNames = |
| 15 | [ |
| 16 | nameof(IdeHealthMountPayload), |
| 17 | nameof(PfdIdeHealthMountContext), |
| 18 | nameof(MfdIdeHealthMountContext), |
| 19 | nameof(IdeHealthBuildText), |
| 20 | nameof(IdeHealthBuildCockpitShort), |
| 21 | nameof(IdeHealthTestsText), |
| 22 | nameof(IdeHealthTestsCockpitShort), |
| 23 | nameof(IdeHealthDebugText), |
| 24 | nameof(IdeHealthDebugCockpitShort), |
| 25 | ]; |
| 26 | |
| 27 | private bool _inIdeHealthRebuild; |
| 28 | private IdeHealthInputSnapshot? _lastIdeHealthInputSnapshot; |
| 29 | private IdeHealthStatusMountPayload? _lastIdeHealthMountPayload; |
| 30 | |
| 31 | /// <summary>Упорядоченные сегменты для <see cref="Views.WorkspaceHealthStripView"/> (поверхность); строит <see cref="IIdeHealthSurfaceCompositor"/> из снимка канала (ADR 0036 п.1→п.3).</summary> |
| 32 | public ObservableCollection<IdeHealthSegment> IdeHealthSegments { get; } = new(); |
| 33 | |
| 34 | /// <summary>Сид DataBus + отображаемых строк git до первого <see cref="RefreshGitSummaryAsync"/> (ADR 0099).</summary> |
| 35 | private void SeedIdeHealthDataBus() |
| 36 | { |
| 37 | _ideDataBus.Publish(new StartupProjectPathChanged(StartupProjectCsprojFullPath)); |
| 38 | _ideDataBus.Publish(new GitStateChanged(Chrome.WorkspaceHealthGitText, Chrome.WorkspaceHealthGitCockpitShort)); |
| 39 | _ideDataBus.Publish(new IdeHostStateChanged( |
| 40 | CSharpLspProcessActive: false, |
| 41 | MarkdownLspProcessActive: false, |
| 42 | CSharpLspHostPresent: false, |
| 43 | MarkdownLspHostPresent: false)); |
| 44 | } |
| 45 | |
| 46 | /// <summary>Каноническое LSP-состояние C#/MD для DataBus, IDE Health (страт C) и ER; только с UI thread.</summary> |
| 47 | private IdeHostStateChanged CaptureIdeHostLspState() => |
| 48 | new( |
| 49 | CSharpLspProcessActive: _csharpLspHost is { IsActive: true }, |
| 50 | MarkdownLspProcessActive: _markdownLspHost is { IsActive: true }, |
| 51 | CSharpLspHostPresent: _csharpLspHost != null, |
| 52 | MarkdownLspHostPresent: _markdownLspHost != null); |
| 53 | |
| 54 | /// <summary>Публикация в IDE DataBus и сразу <see cref="RebuildIdeHealth"/>; вызывать с UI thread (обработчики <see cref="IdeHealthSnapshotUnit"/> согласованы с <see cref="RebuildIdeHealth"/>, ADR 0099).</summary> |
| 55 | private void PublishToIdeDataBusAndRebuild<T>(T evt) |
| 56 | { |
| 57 | _ideDataBus.Publish(evt); |
| 58 | RebuildIdeHealth(); |
| 59 | } |
| 60 | |
| 61 | /// <summary>Состояние сборки на шину после await с <c>ConfigureAwait(false)</c>; гарантирует пересбор полосы IDE Health на UI-потоке.</summary> |
| 62 | internal Task PublishIdeBuildStateOnUiAsync(BuildStateChanged e) => |
| 63 | UiScheduler.Default.InvokeAsync(() => PublishToIdeDataBusAndRebuild(e)); |
| 64 | |
| 65 | /// <summary>LSP → шина + пересбор полосы; вызывать с UI thread.</summary> |
| 66 | private void PublishIdeHostLspToDataBusAndRebuild() => |
| 67 | PublishToIdeDataBusAndRebuild(CaptureIdeHostLspState()); |
| 68 | |
| 69 | /// <summary>Один вход для git → шина + пересбор полосы (без дублирования по <see cref="UiChromeViewModel.WorkspaceHealthGitText"/>).</summary> |
| 70 | private void PublishGitToIdeDataBusAndRebuildIdeHealth() => |
| 71 | PublishToIdeDataBusAndRebuild( |
| 72 | new GitStateChanged(Chrome.WorkspaceHealthGitText, Chrome.WorkspaceHealthGitCockpitShort)); |
| 73 | |
| 74 | /// <summary>Освободить подписки канала (при закрытии окна). Идемпотентно.</summary> |
| 75 | public void ReleaseWorkspaceHealthChannel() |
| 76 | { |
| 77 | if (_workspaceHealth is IDisposable d) |
| 78 | d.Dispose(); |
| 79 | } |
| 80 | |
| 81 | private void RebuildIdeHealth() |
| 82 | { |
| 83 | if (_inIdeHealthRebuild) |
| 84 | return; |
| 85 | _inIdeHealthRebuild = true; |
| 86 | try |
| 87 | { |
| 88 | var snapshot = _workspaceHealth.Build(IdeHealthChannelContext.Default); |
| 89 | _lastIdeHealthInputSnapshot = snapshot; |
| 90 | _lastIdeHealthMountPayload = new IdeHealthStatusMountPayload( |
| 91 | snapshot.Solution.Build.CockpitShort, |
| 92 | snapshot.Solution.Tests.CockpitShort, |
| 93 | snapshot.Solution.Debug.CockpitShort, |
| 94 | SafetyLevel); |
| 95 | _workspaceHealthSurfaceCompositor.Compose( |
| 96 | IdeHealthSegments, |
| 97 | snapshot, |
| 98 | new IdeHealthSurfaceDecision(Enabled: true)); |
| 99 | foreach (var name in IdeHealthRebuildPresentationNames) |
| 100 | OnPropertyChanged(name); |
| 101 | } |
| 102 | finally |
| 103 | { |
| 104 | _inIdeHealthRebuild = false; |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |