| 1 | #nullable enable |
| 2 | using CascadeIDE.Features.Launch.Application; |
| 3 | using System.Collections.ObjectModel; |
| 4 | using CommunityToolkit.Mvvm.ComponentModel; |
| 5 | using CommunityToolkit.Mvvm.Input; |
| 6 | |
| 7 | namespace CascadeIDE.ViewModels; |
| 8 | |
| 9 | /// <summary>Селектор launch profile, импорт <c>launchSettings.json</c> (ADR 0090).</summary> |
| 10 | public partial class MainWindowViewModel |
| 11 | { |
| 12 | public ObservableCollection<string> LaunchProfileIds { get; } = new(); |
| 13 | |
| 14 | [ObservableProperty] |
| 15 | private string? _selectedLaunchProfileId; |
| 16 | |
| 17 | private bool _suspendLaunchProfileSelectionPersistence; |
| 18 | |
| 19 | /// <summary>Есть ≥1 именованного профиля в <c>launch-profiles.toml</c>.</summary> |
| 20 | public bool ShowLaunchProfilePicker => LaunchProfileIds.Count > 0; |
| 21 | |
| 22 | partial void OnSelectedLaunchProfileIdChanged(string? value) |
| 23 | { |
| 24 | if (_suspendLaunchProfileSelectionPersistence) |
| 25 | return; |
| 26 | var sln = Workspace.SolutionPath; |
| 27 | if (string.IsNullOrEmpty(sln) || string.IsNullOrEmpty(value)) |
| 28 | return; |
| 29 | _ = LaunchProfilesStore.TrySetActiveProfile(sln, value, out _); |
| 30 | OnPropertyChanged(nameof(StartupProjectBanner)); |
| 31 | } |
| 32 | |
| 33 | public void RefreshLaunchProfilePickerFromStore() |
| 34 | { |
| 35 | _suspendLaunchProfileSelectionPersistence = true; |
| 36 | try |
| 37 | { |
| 38 | LaunchProfileIds.Clear(); |
| 39 | var sln = Workspace.SolutionPath; |
| 40 | if (string.IsNullOrEmpty(sln)) |
| 41 | { |
| 42 | SelectedLaunchProfileId = null; |
| 43 | return; |
| 44 | } |
| 45 | |
| 46 | if (!LaunchProfilesStore.TryGetOrderedProfileIds(sln, out var names, out _)) |
| 47 | { |
| 48 | SelectedLaunchProfileId = null; |
| 49 | return; |
| 50 | } |
| 51 | |
| 52 | foreach (var n in names) |
| 53 | LaunchProfileIds.Add(n); |
| 54 | |
| 55 | if (LaunchProfilesStore.TryGetActiveProfileName(sln, out var active, out _) && !string.IsNullOrEmpty(active)) |
| 56 | { |
| 57 | var match = LaunchProfileIds.FirstOrDefault(x => string.Equals(x, active, StringComparison.OrdinalIgnoreCase)); |
| 58 | SelectedLaunchProfileId = match ?? LaunchProfileIds[0]; |
| 59 | } |
| 60 | else |
| 61 | SelectedLaunchProfileId = LaunchProfileIds[0]; |
| 62 | } |
| 63 | finally |
| 64 | { |
| 65 | _suspendLaunchProfileSelectionPersistence = false; |
| 66 | } |
| 67 | |
| 68 | OnPropertyChanged(nameof(ShowLaunchProfilePicker)); |
| 69 | OnPropertyChanged(nameof(StartupProjectBanner)); |
| 70 | } |
| 71 | |
| 72 | [RelayCommand(CanExecute = nameof(CanImportLaunchSettingsFromSelection))] |
| 73 | private async Task ImportLaunchSettingsFromSelectionAsync() |
| 74 | { |
| 75 | var item = Workspace.SelectedSolutionItem; |
| 76 | var path = item?.FullPath; |
| 77 | if (string.IsNullOrEmpty(path) || !path.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase)) |
| 78 | return; |
| 79 | |
| 80 | var sln = Workspace.SolutionPath; |
| 81 | if (string.IsNullOrEmpty(sln)) |
| 82 | return; |
| 83 | |
| 84 | var solutionDir = Services.BreakpointsFileService.GetWorkspaceRoot(sln); |
| 85 | if (string.IsNullOrEmpty(solutionDir)) |
| 86 | return; |
| 87 | |
| 88 | if (!LaunchProjectRelativePath.TryGetRelativeToSolutionDirectory(solutionDir, path, out var rel, out var pathErr)) |
| 89 | { |
| 90 | await ShowDebugInfoAsync("launchSettings.json", pathErr).ConfigureAwait(false); |
| 91 | return; |
| 92 | } |
| 93 | |
| 94 | if (!LaunchProfilesStore.TryImportFromLaunchSettings(sln, rel, out var n, out var err) || !string.IsNullOrEmpty(err)) |
| 95 | { |
| 96 | await ShowDebugInfoAsync("Импорт launch profiles", err ?? "import_failed").ConfigureAwait(false); |
| 97 | return; |
| 98 | } |
| 99 | |
| 100 | RefreshLaunchProfilePickerFromStore(); |
| 101 | await ShowDebugInfoAsync("Импорт launch profiles", $"Скопировано профилей (Kestrel/Project) в {LaunchProfilesStore.FileName}: {n}.").ConfigureAwait(false); |
| 102 | } |
| 103 | |
| 104 | private bool CanImportLaunchSettingsFromSelection() => |
| 105 | !string.IsNullOrEmpty(Workspace.SolutionPath) && |
| 106 | !string.IsNullOrEmpty(Workspace.SelectedSolutionItem?.FullPath) && |
| 107 | Workspace.SelectedSolutionItem!.FullPath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase); |
| 108 | } |
| 109 | |