protected async Task UpdateProfilesAsync(string updatedActiveProfileName) { try { // If the name of the new active profile wasn't provided we'll continue to use the // current one. if (updatedActiveProfileName == null) { ProjectDebugger props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetProjectDebuggerPropertiesAsync(); if (await props.ActiveDebugProfile.GetValueAsync() is IEnumValue activeProfileVal) { updatedActiveProfileName = activeProfileVal.Name; } } LaunchSettingsData launchSettingData = await GetLaunchSettingsAsync(); // If there are no profiles, we will add a default profile to run the project. W/o it our debugger // won't be called on F5 and the user will see a poor error message if (launchSettingData.Profiles.Count == 0) { launchSettingData.Profiles.Add(new LaunchProfileData() { Name = Path.GetFileNameWithoutExtension(_commonProjectServices.Project.FullPath), CommandName = RunProjectCommandName }); } // If we have a previous snapshot merge in in-memory profiles ILaunchSettings prevSnapshot = CurrentSnapshot; if (prevSnapshot != null) { MergeExistingInMemoryProfiles(launchSettingData, prevSnapshot); MergeExistingInMemoryGlobalSettings(launchSettingData, prevSnapshot); } var newSnapshot = new LaunchSettings(launchSettingData, updatedActiveProfileName); FinishUpdate(newSnapshot); } catch (Exception ex) { // Errors are added as error list entries. We don't want to throw out of here // However, if we have never created a snapshot it means there is some error in the file and we want // to have the user see that, so we add a dummy profile which will bind to an existing debugger which will // display the error when run if (CurrentSnapshot == null) { var errorProfile = new LaunchProfile { Name = Resources.NoActionProfileName, CommandName = ErrorProfileCommandName, DoNotPersist = true, OtherSettings = ImmutableStringDictionary <object> .EmptyOrdinal.Add("ErrorString", ex.Message) }; var snapshot = new LaunchSettings(new[] { errorProfile }, null, errorProfile.Name); FinishUpdate(snapshot); } } }
/// <summary> /// Does the processing to update the profiles when changes have been made to either the file, the active profile or projectData. Note that /// any of the parmaters can be null. This code must handle that correctly. Setting existingProfiles to null effectively re-intilaizes the /// entire list. /// </summary> protected async Task UpdateProfilesAsync(string activeProfile) { try { // If no active profile specified, try to get one if (activeProfile == null) { var props = await CommonProjectServices.ActiveConfiguredProjectProperties.GetProjectDebuggerPropertiesAsync().ConfigureAwait(true); var activeProfileVal = await props.ActiveDebugProfile.GetValueAsync().ConfigureAwait(true) as IEnumValue; if (activeProfileVal != null) { activeProfile = activeProfileVal.Name; } } var launchSettingData = GetLaunchSettings(); // If there are no profiles, we will add a default profile to run the prroject. W/o it our debugger // won't be called on F5 and the user will see a poor error message if (launchSettingData.Profiles.Count == 0) { launchSettingData.Profiles.Add(new LaunchProfileData() { Name = Path.GetFileNameWithoutExtension(CommonProjectServices.Project.FullPath), CommandName = RunProjectCommandName }); } var newSnapshot = new LaunchSettings(launchSettingData, activeProfile); FinishUpdate(newSnapshot); } catch (Exception ex) { // Errors are added as error list entries. We don't want to throw out of here // However, if we have never created a snapshot it means there is some error in the file and we want // to have the user see that, so we add a dummy profile which will bind to an existing debugger which will // display the error when run if (CurrentSnapshot == null) { var errorProfile = new LaunchProfile() { Name = Resources.NoActionProfileName, CommandName = LaunchSettingsProvider.ErrorProfileCommandName }; errorProfile.OtherSettings = ImmutableDictionary <string, object> .Empty.Add("ErrorString", ex.Message); var snapshot = new LaunchSettings(new List <ILaunchProfile>() { errorProfile }, null, errorProfile.Name); FinishUpdate(snapshot); } } }
/// <summary> /// Removes the specified global setting and saves the settings to disk /// </summary> public async Task RemoveGlobalSettingAsync(string settingName) { var currentSettings = await GetSnapshotThrowIfErrors().ConfigureAwait(false); if (currentSettings.GlobalSettings.TryGetValue(settingName, out object currentValue)) { var globalSettings = currentSettings.GlobalSettings.Remove(settingName); var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings, currentSettings.ActiveProfile?.Name); await UpdateAndSaveSettingsAsync(newSnapshot).ConfigureAwait(false); } }
/// <summary> /// Called when the active profile has changed. If there is a current snapshot it just updates that. Otherwwise, it creates /// a new snapshot /// </summary> protected async Task UpdateActiveProfileInSnapshotAsync(string activeProfile) { var snapshot = CurrentSnapshot; if (snapshot == null || await SettingsFileHasChangedAsync().ConfigureAwait(false)) { await UpdateProfilesAsync(activeProfile).ConfigureAwait(false); return; } var newSnapshot = new LaunchSettings(snapshot.Profiles, snapshot.GlobalSettings, activeProfile); await FinishUpdateAsync(newSnapshot, ensureProfileProperty: false).ConfigureAwait(false); }
/// <summary> /// Replaces the current set of profiles with the contents of profiles. If changes were /// made, the file will be checked out and saved. /// </summary> public async Task UpdateAndSaveSettingsAsync(ILaunchSettings newSettings) { // Make sure the profiles are copied. We don't want them to mutate. ILaunchSettings newSnapshot = new LaunchSettings(newSettings.Profiles, newSettings.GlobalSettings, newSettings.ActiveProfile?.Name); // Being saved and changeMade are different since the active profile change does not require them to be saved. await CheckoutSettingsFileAsync().ConfigureAwait(false); SaveSettingsToDisk(newSettings); FinishUpdate(newSnapshot); }
/// <summary> /// Removes the specified profile from the list and saves to disk. /// </summary> public async Task RemoveProfileAsync(string profileName) { var currentSettings = await GetSnapshotThrowIfErrors().ConfigureAwait(false); var existingProfile = currentSettings.Profiles.FirstOrDefault(p => LaunchProfile.IsSameProfileName(p.Name, profileName)); if (existingProfile != null) { var profiles = currentSettings.Profiles.Remove(existingProfile); var newSnapshot = new LaunchSettings(profiles, currentSettings.GlobalSettings, currentSettings.ActiveProfile?.Name); await UpdateAndSaveSettingsAsync(newSnapshot).ConfigureAwait(false); } }
/// <summary> /// Replaces the current set of profiles with the contents of profiles. If changes were /// made, the file will be checked out and saved. Note it ignores the value of the active profile /// as this setting is controlled by a user property. /// </summary> private async Task UpdateAndSaveSettingsInternalAsync(ILaunchSettings newSettings) { await CheckoutSettingsFileAsync().ConfigureAwait(false); // Make sure the profiles are copied. We don't want them to mutate. var activeProfileName = ActiveProfile?.Name; ILaunchSettings newSnapshot = new LaunchSettings(newSettings.Profiles, newSettings.GlobalSettings, activeProfileName); SaveSettingsToDisk(newSettings); FinishUpdate(newSnapshot); }
/// <summary> /// Removes the specified global setting and saves the settings to disk /// </summary> public async Task RemoveGlobalSettingAsync(string settingName) { // Updates need to be sequenced await _sequentialTaskQueue.ExecuteTask(async() => { var currentSettings = await GetSnapshotThrowIfErrors().ConfigureAwait(false); if (currentSettings.GlobalSettings.TryGetValue(settingName, out object currentValue)) { var globalSettings = currentSettings.GlobalSettings.Remove(settingName); var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings, currentSettings.ActiveProfile?.Name); await UpdateAndSaveSettingsInternalAsync(newSnapshot).ConfigureAwait(false); } }).ConfigureAwait(false); }
/// <summary> /// Called when the active profile has changed. If there is a current snapshot it just updates that. Otherwise, it creates /// a new snapshot /// </summary> protected async Task UpdateActiveProfileInSnapshotAsync(string updatedActiveProfileName) { ILaunchSettings snapshot = CurrentSnapshot; if (snapshot == null || await SettingsFileHasChangedAsync()) { await UpdateProfilesAsync(updatedActiveProfileName); return; } var newSnapshot = new LaunchSettings(snapshot.Profiles, snapshot.GlobalSettings, updatedActiveProfileName); FinishUpdate(newSnapshot); }
/// <summary> /// Replaces the current set of profiles with the contents of profiles. If changes were /// made, the file will be checked out and saved. Note it ignores the value of the active profile /// as this setting is controlled by a user property. /// </summary> private async Task UpdateAndSaveSettingsInternalAsync(ILaunchSettings newSettings, bool persistToDisk = true) { await CheckoutSettingsFileAsync().ConfigureAwait(false); // Make sure the profiles are copied. We don't want them to mutate. var activeProfileName = ActiveProfile?.Name; ILaunchSettings newSnapshot = new LaunchSettings(newSettings.Profiles, newSettings.GlobalSettings, activeProfileName); if (persistToDisk) { await SaveSettingsToDiskAsync(newSettings).ConfigureAwait(false); } await FinishUpdateAsync(newSnapshot, ensureProfileProperty: true).ConfigureAwait(false); }
public Task RemoveGlobalSettingAsync(string settingName) { // Updates need to be sequenced return(_sequentialTaskQueue.ExecuteTask(async() => { ILaunchSettings currentSettings = await GetSnapshotThrowIfErrors(); if (currentSettings.GlobalSettings.TryGetValue(settingName, out object currentValue)) { bool saveToDisk = !currentValue.IsInMemoryObject(); ImmutableDictionary <string, object> globalSettings = currentSettings.GlobalSettings.Remove(settingName); var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings, currentSettings.ActiveProfile?.Name); await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); } })); }
/// <summary> /// Removes the specified profile from the list and saves to disk. /// </summary> public async Task RemoveProfileAsync(string profileName) { // Updates need to be sequenced await _sequentialTaskQueue.ExecuteTask(async() => { var currentSettings = await GetSnapshotThrowIfErrors().ConfigureAwait(false); var existingProfile = currentSettings.Profiles.FirstOrDefault(p => LaunchProfile.IsSameProfileName(p.Name, profileName)); if (existingProfile != null) { var profiles = currentSettings.Profiles.Remove(existingProfile); var newSnapshot = new LaunchSettings(profiles, currentSettings.GlobalSettings, currentSettings.ActiveProfile?.Name); await UpdateAndSaveSettingsInternalAsync(newSnapshot).ConfigureAwait(false); } }).ConfigureAwait(false); }
public Task AddOrUpdateProfileAsync(ILaunchProfile profile, bool addToFront) { // Updates need to be sequenced return(_sequentialTaskQueue.ExecuteTask(async() => { ILaunchSettings currentSettings = await GetSnapshotThrowIfErrors(); ILaunchProfile existingProfile = null; int insertionIndex = 0; foreach (ILaunchProfile p in currentSettings.Profiles) { if (LaunchProfile.IsSameProfileName(p.Name, profile.Name)) { existingProfile = p; break; } insertionIndex++; } ImmutableList <ILaunchProfile> profiles; if (existingProfile != null) { profiles = currentSettings.Profiles.Remove(existingProfile); } else { profiles = currentSettings.Profiles; } if (addToFront) { profiles = profiles.Insert(0, new LaunchProfile(profile)); } else { // Insertion index will be set to the current count (end of list) if an existing item was not found otherwise // it will point to where the previous one was found profiles = profiles.Insert(insertionIndex, new LaunchProfile(profile)); } // If the new profile is in-memory only, we don't want to touch the disk unless it replaces an existing disk based // profile bool saveToDisk = !profile.IsInMemoryObject() || (existingProfile != null && !existingProfile.IsInMemoryObject()); var newSnapshot = new LaunchSettings(profiles, currentSettings?.GlobalSettings, currentSettings?.ActiveProfile?.Name); await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); })); }
public Task RemoveProfileAsync(string profileName) { // Updates need to be sequenced return(_sequentialTaskQueue.ExecuteTask(async() => { ILaunchSettings currentSettings = await GetSnapshotThrowIfErrors(); ILaunchProfile existingProfile = currentSettings.Profiles.FirstOrDefault(p => LaunchProfile.IsSameProfileName(p.Name, profileName)); if (existingProfile != null) { ImmutableList <ILaunchProfile> profiles = currentSettings.Profiles.Remove(existingProfile); // If the new profile is in-memory only, we don't want to touch the disk bool saveToDisk = !existingProfile.IsInMemoryObject(); var newSnapshot = new LaunchSettings(profiles, currentSettings.GlobalSettings, currentSettings.ActiveProfile?.Name); await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); } })); }
/// <summary> /// Replaces the current set of profiles with the contents of profiles. If changes were /// made, the file will be checked out and saved. Note it ignores the value of the active profile /// as this setting is controlled by a user property. /// </summary> protected async Task UpdateAndSaveSettingsInternalAsync(ILaunchSettings newSettings, bool persistToDisk = true) { if (persistToDisk) { await CheckoutSettingsFileAsync(); } // Make sure the profiles are copied. We don't want them to mutate. string?activeProfileName = ActiveProfile?.Name; ILaunchSettings newSnapshot = new LaunchSettings(newSettings.Profiles, newSettings.GlobalSettings, activeProfileName); if (persistToDisk) { await SaveSettingsToDiskAsync(newSettings); } FinishUpdate(newSnapshot); }
/// <summary> /// Adds or updates the global settings represented by settingName. Saves the /// updated settings to disk. Note that the settings object must be serializable. /// </summary> public async Task AddOrUpdateGlobalSettingAsync(string settingName, object settingContent) { var currentSettings = await GetSnapshotThrowIfErrors().ConfigureAwait(false); ImmutableDictionary <string, object> globalSettings = ImmutableDictionary <string, object> .Empty; if (currentSettings.GlobalSettings.TryGetValue(settingName, out object currentValue)) { globalSettings = currentSettings.GlobalSettings.Remove(settingName); } else { globalSettings = currentSettings.GlobalSettings; } var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings.Add(settingName, settingContent), currentSettings.ActiveProfile?.Name); await UpdateAndSaveSettingsAsync(newSnapshot).ConfigureAwait(false); }
/// <summary> /// Adds the given profile to the list and saves to disk. If a profile with the same /// name exists (case sensitive), it will be replaced with the new profile. If addToFront is /// true the profile will be the first one in the list. This is useful since quite often callers want /// their just added profile to be listed first in the start menu. If addToFront is false but there is /// an existing profile, the new one will be inserted at the same location rather than at the end. /// </summary> public async Task AddOrUpdateProfileAsync(ILaunchProfile profile, bool addToFront) { // Updates need to be sequenced await _sequentialTaskQueue.ExecuteTask(async() => { var currentSettings = await GetSnapshotThrowIfErrors().ConfigureAwait(false); ILaunchProfile existingProfile = null; int insertionIndex = 0; foreach (var p in currentSettings.Profiles) { if (LaunchProfile.IsSameProfileName(p.Name, profile.Name)) { existingProfile = p; break; } insertionIndex++; } ImmutableList <ILaunchProfile> profiles; if (existingProfile != null) { profiles = currentSettings.Profiles.Remove(existingProfile); } else { profiles = currentSettings.Profiles; } if (addToFront) { profiles = profiles.Insert(0, new LaunchProfile(profile)); } else { // Insertion index will be set to the current count (end of list) if an existing item was not found otherwise // it will point to where the previous one was found profiles = profiles.Insert(insertionIndex, new LaunchProfile(profile)); } var newSnapshot = new LaunchSettings(profiles, currentSettings?.GlobalSettings, currentSettings?.ActiveProfile?.Name); await UpdateAndSaveSettingsInternalAsync(newSnapshot).ConfigureAwait(false); }).ConfigureAwait(false); }
public void LaunchSettings_CtorTests() { var profiles = new List <LaunchProfile>() { new LaunchProfile() { Name = "abc", CommandLineArgs = "test" }, new LaunchProfile() { Name = "def" }, new LaunchProfile() { Name = "ghi" }, new LaunchProfile() { Name = "foo" }, }; var globals = new Dictionary <string, object>() { { "var1", true }, { "var2", "some string" } }; var settings = new LaunchSettings(profiles, null, null); Assert.True(settings.ActiveProfile.Name == "abc"); Assert.Equal(profiles.Count, settings.Profiles.Count); Assert.Empty(settings.GlobalSettings); settings = new LaunchSettings(profiles, null, "ghi"); Assert.True(settings.ActiveProfile.Name == "ghi"); // Test settings = new LaunchSettings(profiles, globals, "foo"); Assert.Equal(globals.Count, settings.GlobalSettings.Count); }
public Task AddOrUpdateGlobalSettingAsync(string settingName, object settingContent) { // Updates need to be sequenced return(_sequentialTaskQueue.ExecuteTask(async() => { ILaunchSettings currentSettings = await GetSnapshotThrowIfErrors(); ImmutableDictionary <string, object> globalSettings = ImmutableStringDictionary <object> .EmptyOrdinal; if (currentSettings.GlobalSettings.TryGetValue(settingName, out object currentValue)) { globalSettings = currentSettings.GlobalSettings.Remove(settingName); } else { globalSettings = currentSettings.GlobalSettings; } bool saveToDisk = !settingContent.IsInMemoryObject() || (currentValue != null && !currentValue.IsInMemoryObject()); var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings.Add(settingName, settingContent), currentSettings.ActiveProfile?.Name); await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); })); }