// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.SqlTools.DataProtocol.Contracts.Workspace; using Microsoft.SqlTools.Hosting; using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.Hosting.Utility; namespace Microsoft.SqlTools.CoreServices.Workspace { /// /// Class for handling requests/events that deal with the state of the workspace, including the /// opening and closing of files, the changing of configuration, etc. /// /// /// The type of the class used for serializing and deserializing the configuration. Must be the /// actual type of the instance otherwise deserialization will be incomplete. /// public class SettingsService where TConfig : class, new() { #region Singleton Instance Implementation private static Lazy> instance = new Lazy>(() => new SettingsService()); public static SettingsService Instance { get { return instance.Value; } } /// /// Default, parameterless constructor. /// TODO: Figure out how to make this truely singleton even with dependency injection for tests /// public SettingsService() { ConfigChangeCallbacks = new List(); CurrentSettings = new TConfig(); } #endregion #region Properties /// /// Current settings for the workspace /// public TConfig CurrentSettings { get; internal set; } /// /// Delegate for callbacks that occur when the configuration for the workspace changes /// /// The settings that were just set /// The settings before they were changed /// Context of the event that triggered the callback /// public delegate Task ConfigChangeCallback(TConfig newSettings, TConfig oldSettings, EventContext eventContext); /// /// List of callbacks to call when the configuration of the workspace changes /// private List ConfigChangeCallbacks { get; set; } #endregion #region Public Methods public void InitializeService(IServiceHost serviceHost) { // Register the handlers for when changes to the workspae occur serviceHost.SetAsyncEventHandler(DidChangeConfigurationNotification.Type, HandleDidChangeConfigurationNotification); } /// /// Adds a new task to be called when the configuration has been changed. Use this to /// handle changing configuration and changing the current configuration. /// /// Task to handle the request public void RegisterConfigChangeCallback(ConfigChangeCallback task) { ConfigChangeCallbacks.Add(task); } #endregion #region Event Handlers /// /// Handles the configuration change event /// internal async Task HandleDidChangeConfigurationNotification( DidChangeConfigurationParams configChangeParams, EventContext eventContext) { try { Logger.Write(TraceEventType.Verbose, "HandleDidChangeConfigurationNotification"); // Propagate the changes to the event handlers var configUpdateTasks = ConfigChangeCallbacks.Select( t => t(configChangeParams.Settings, CurrentSettings, eventContext)); await Task.WhenAll(configUpdateTasks); } catch (Exception ex) { Logger.Write(TraceEventType.Error, "Unknown error " + ex.ToString()); // Swallow exceptions here to prevent us from crashing // TODO: this probably means the ScriptFile model is in a bad state or out of sync with the actual file; we should recover here return; } } #endregion } }