// // 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.Linq; using System.Threading.Tasks; using System.Collections.Generic; using Microsoft.SqlTools.EditorServices.Utility; using Microsoft.SqlTools.ServiceLayer.Hosting.Contracts; using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel; using System.Reflection; namespace Microsoft.SqlTools.ServiceLayer.Hosting { /// /// SQL Tools VS Code Language Server request handler. Provides the entire JSON RPC /// implementation for sending/receiving JSON requests and dispatching the requests to /// handlers that are registered prior to startup. /// public sealed class ServiceHost : ServiceHostBase { #region Singleton Instance Code /// /// Singleton instance of the service host for internal storage /// private static readonly Lazy instance = new Lazy(() => new ServiceHost()); /// /// Current instance of the ServiceHost /// public static ServiceHost Instance { get { return instance.Value; } } /// /// Constructs new instance of ServiceHost using the host and profile details provided. /// Access is private to ensure only one instance exists at a time. /// private ServiceHost() : base(new StdioServerChannel()) { // Initialize the shutdown activities shutdownCallbacks = new List(); initializeCallbacks = new List(); } /// /// Provide initialization that must occur after the service host is started /// public void Initialize() { // Register the requests that this service host will handle this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest); this.SetRequestHandler(ShutdownRequest.Type, this.HandleShutdownRequest); this.SetRequestHandler(VersionRequest.Type, HandleVersionRequest); } #endregion #region Member Variables public delegate Task ShutdownCallback(object shutdownParams, RequestContext shutdownRequestContext); public delegate Task InitializeCallback(InitializeRequest startupParams, RequestContext requestContext); private readonly List shutdownCallbacks; private readonly List initializeCallbacks; private static readonly Version serviceVersion = Assembly.GetEntryAssembly().GetName().Version; #endregion #region Public Methods /// /// Adds a new callback to be called when the shutdown request is submitted /// /// Callback to perform when a shutdown request is submitted public void RegisterShutdownTask(ShutdownCallback callback) { shutdownCallbacks.Add(callback); } /// /// Add a new method to be called when the initialize request is submitted /// /// Callback to perform when an initialize request is submitted public void RegisterInitializeTask(InitializeCallback callback) { initializeCallbacks.Add(callback); } #endregion #region Request Handlers /// /// Handles the shutdown event for the Language Server /// private async Task HandleShutdownRequest(object shutdownParams, RequestContext requestContext) { Logger.Write(LogLevel.Normal, "Service host is shutting down..."); // Call all the shutdown methods provided by the service components Task[] shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext)).ToArray(); await Task.WhenAll(shutdownTasks); } /// /// Handles the initialization request /// /// /// /// private async Task HandleInitializeRequest(InitializeRequest initializeParams, RequestContext requestContext) { Logger.Write(LogLevel.Verbose, "HandleInitializationRequest"); // Call all tasks that registered on the initialize request var initializeTasks = initializeCallbacks.Select(t => t(initializeParams, requestContext)); await Task.WhenAll(initializeTasks); // TODO: Figure out where this needs to go to be agnostic of the language // Send back what this server can do await requestContext.SendResult( new InitializeResult { Capabilities = new ServerCapabilities { TextDocumentSync = TextDocumentSyncKind.Incremental, DefinitionProvider = true, ReferencesProvider = true, DocumentHighlightProvider = true, DocumentSymbolProvider = true, WorkspaceSymbolProvider = true, HoverProvider = true, CompletionProvider = new CompletionOptions { ResolveProvider = true, TriggerCharacters = new string[] { ".", "-", ":", "\\" } }, SignatureHelpProvider = new SignatureHelpOptions { TriggerCharacters = new string[] { " " } // TODO: Other characters here? } } }); } /// /// Handles the version request. Sends back the server version as result. /// private static async Task HandleVersionRequest( object versionRequestParams, RequestContext requestContext) { Logger.Write(LogLevel.Verbose, "HandleVersionRequest"); await requestContext.SendResult(serviceVersion.ToString()); } #endregion } }