diff --git a/ServiceHost/.vscode/launch.json b/ServiceHost/.vscode/launch.json index ea4209f7..18ebbb27 100644 --- a/ServiceHost/.vscode/launch.json +++ b/ServiceHost/.vscode/launch.json @@ -18,7 +18,7 @@ "type": "coreclr", "request": "attach", "requireExactSource": false, - "processId": 14720 + "processId": 17264 } ] } \ No newline at end of file diff --git a/ServiceHost/LanguageServer/TextDocument.cs b/ServiceHost/LanguageServer/TextDocument.cs index 9b477c96..9f477374 100644 --- a/ServiceHost/LanguageServer/TextDocument.cs +++ b/ServiceHost/LanguageServer/TextDocument.cs @@ -61,12 +61,27 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer public class DidChangeTextDocumentParams : TextDocumentIdentifier { + public TextDocumentUriChangeEvent TextDocument { get; set; } + /// /// Gets or sets the list of changes to the document content. /// public TextDocumentChangeEvent[] ContentChanges { get; set; } } + public class TextDocumentUriChangeEvent + { + /// + /// Gets or sets the Uri of the changed text document + /// + public string Uri { get; set; } + + /// + /// Gets or sets the Version of the changed text document + /// + public int Version { get; set; } + } + public class TextDocumentChangeEvent { /// diff --git a/ServiceHost/MessageProtocol/MessageDispatcher.cs b/ServiceHost/MessageProtocol/MessageDispatcher.cs index 7d2d9c4c..21c179e2 100644 --- a/ServiceHost/MessageProtocol/MessageDispatcher.cs +++ b/ServiceHost/MessageProtocol/MessageDispatcher.cs @@ -18,7 +18,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol #region Fields private ChannelBase protocolChannel; - // private AsyncQueue messagesToWrite; + private AsyncContextThread messageLoopThread; private Dictionary> requestHandlers = diff --git a/ServiceHost/MessageProtocol/Serializers/JsonRpcMessageSerializer.cs b/ServiceHost/MessageProtocol/Serializers/JsonRpcMessageSerializer.cs index edf14712..fa1d1518 100644 --- a/ServiceHost/MessageProtocol/Serializers/JsonRpcMessageSerializer.cs +++ b/ServiceHost/MessageProtocol/Serializers/JsonRpcMessageSerializer.cs @@ -3,10 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using System; namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Serializers { diff --git a/ServiceHost/MessageProtocol/Serializers/V8MessageSerializer.cs b/ServiceHost/MessageProtocol/Serializers/V8MessageSerializer.cs index 4088a223..941e249a 100644 --- a/ServiceHost/MessageProtocol/Serializers/V8MessageSerializer.cs +++ b/ServiceHost/MessageProtocol/Serializers/V8MessageSerializer.cs @@ -3,8 +3,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; diff --git a/ServiceHost/Server/LanguageServer.cs b/ServiceHost/Server/LanguageServer.cs index 4f7367a4..2f0f7963 100644 --- a/ServiceHost/Server/LanguageServer.cs +++ b/ServiceHost/Server/LanguageServer.cs @@ -8,17 +8,23 @@ using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Channel; using Microsoft.SqlTools.EditorServices.Session; using System.Threading.Tasks; using Microsoft.SqlTools.EditorServices.Utility; +using System.Collections.Generic; +using System.Text; namespace Microsoft.SqlTools.EditorServices.Protocol.Server { public class LanguageServer : LanguageServerBase { + private EditorSession editorSession; + /// /// Provides details about the host application. /// public LanguageServer(HostDetails hostDetails, ProfilePaths profilePaths) : base(new StdioServerChannel()) { + this.editorSession = new EditorSession(); + this.editorSession.StartSession(hostDetails, profilePaths); } protected override void Initialize() @@ -43,6 +49,14 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server protected override async Task Shutdown() { + Logger.Write(LogLevel.Normal, "Language service is shutting down..."); + + if (this.editorSession != null) + { + this.editorSession.Dispose(); + this.editorSession = null; + } + await Task.FromResult(true); } @@ -80,11 +94,46 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server }); } + /// + /// Handles text document change events + /// + /// + /// + /// protected Task HandleDidChangeTextDocumentNotification( DidChangeTextDocumentParams textChangeParams, EventContext eventContext) { - Logger.Write(LogLevel.Normal, "HandleDidChangeTextDocumentNotification"); + StringBuilder msg = new StringBuilder(); + msg.Append("HandleDidChangeTextDocumentNotification"); + List changedFiles = new List(); + + // A text change notification can batch multiple change requests + foreach (var textChange in textChangeParams.ContentChanges) + { + string fileUri = textChangeParams.TextDocument.Uri; + msg.AppendLine(); + msg.Append(" File: "); + msg.Append(fileUri); + + ScriptFile changedFile = editorSession.Workspace.GetFile(fileUri); + + // changedFile.ApplyChange( + // GetFileChangeDetails( + // textChange.Range.Value, + // textChange.Text)); + + // changedFiles.Add(changedFile); + } + + Logger.Write(LogLevel.Normal, msg.ToString()); + + // // TODO: Get all recently edited files in the workspace + // this.RunScriptDiagnostics( + // changedFiles.ToArray(), + // editorSession, + // eventContext); + return Task.FromResult(true); } diff --git a/ServiceHost/Session/EditorSession.cs b/ServiceHost/Session/EditorSession.cs index 21c1b577..4b6c9a49 100644 --- a/ServiceHost/Session/EditorSession.cs +++ b/ServiceHost/Session/EditorSession.cs @@ -3,11 +3,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -// using Microsoft.SqlTools.EditorServices.Console; -// using Microsoft.SqlTools.EditorServices.Extensions; +using System; using Microsoft.SqlTools.EditorServices.Session; -// using Microsoft.SqlTools.EditorServices.Utility; -// using System.IO; namespace Microsoft.SqlTools.EditorServices { @@ -15,12 +12,8 @@ namespace Microsoft.SqlTools.EditorServices /// Manages a single session for all editor services. This /// includes managing all open script files for the session. /// - public class EditorSession + public class EditorSession : IDisposable { - public void StartSession(HostDetails hostDetails, ProfilePaths profilePaths) - { - } -#if false #region Properties /// @@ -33,6 +26,59 @@ namespace Microsoft.SqlTools.EditorServices /// public SqlToolsContext SqlToolsContext { get; private set; } + #endregion + + #region Public Methods + + /// + /// Starts the session using the provided IConsoleHost implementation + /// for the ConsoleService. + /// + /// + /// Provides details about the host application. + /// + /// + /// An object containing the profile paths for the session. + /// + public void StartSession(HostDetails hostDetails, ProfilePaths profilePaths) + { + // Initialize all services + this.SqlToolsContext = new SqlToolsContext(hostDetails, profilePaths); + + + // this.LanguageService = new LanguageService(this.SqlToolsContext); + // this.DebugService = new DebugService(this.SqlToolsContext); + // this.ConsoleService = new ConsoleService(this.SqlToolsContext); + // this.ExtensionService = new ExtensionService(this.SqlToolsContext); + + // this.InstantiateAnalysisService(); + + // Create a workspace to contain open files + this.Workspace = new Workspace(this.SqlToolsContext.SqlToolsVersion); + } + + #endregion + + #region IDisposable Implementation + + /// + /// Disposes of any Runspaces that were created for the + /// services used in this session. + /// + public void Dispose() + { + } + + #endregion + + +#if false + #region Properties + + + + + /// /// Gets the LanguageService instance for this session. /// diff --git a/ServiceHost/Session/SqlToolsContext.cs b/ServiceHost/Session/SqlToolsContext.cs new file mode 100644 index 00000000..d8016afd --- /dev/null +++ b/ServiceHost/Session/SqlToolsContext.cs @@ -0,0 +1,25 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; + +namespace Microsoft.SqlTools.EditorServices.Session +{ + public class SqlToolsContext + { + /// + /// Gets the PowerShell version of the current runspace. + /// + public Version SqlToolsVersion + { + get; private set; + } + + public SqlToolsContext(HostDetails hostDetails, ProfilePaths profilePaths) + { + + } + } +} diff --git a/ServiceHost/Workspace/ScriptFile.cs b/ServiceHost/Workspace/ScriptFile.cs index c68ecb96..7b041832 100644 --- a/ServiceHost/Workspace/ScriptFile.cs +++ b/ServiceHost/Workspace/ScriptFile.cs @@ -18,6 +18,30 @@ namespace Microsoft.SqlTools.EditorServices /// public class ScriptFile { + public ScriptFile( + string filePath, + string clientFilePath, + TextReader textReader, + Version SqlToolsVersion) + { + } + + /// + /// Creates a new ScriptFile instance with the specified file contents. + /// + /// The path at which the script file resides. + /// The path which the client uses to identify the file. + /// The initial contents of the script file. + /// The version of SqlTools for which the script is being parsed. + public ScriptFile( + string filePath, + string clientFilePath, + string initialBuffer, + Version SqlToolsVersion) + { + } + + #if false #region Private Fields diff --git a/ServiceHost/Workspace/Workspace.cs b/ServiceHost/Workspace/Workspace.cs index b8635ef2..6188a806 100644 --- a/ServiceHost/Workspace/Workspace.cs +++ b/ServiceHost/Workspace/Workspace.cs @@ -6,9 +6,9 @@ using Microsoft.SqlTools.EditorServices.Utility; using System; using System.Collections.Generic; -using System.Linq; using System.IO; using System.Text; +using System.Text.RegularExpressions; namespace Microsoft.SqlTools.EditorServices { @@ -18,8 +18,7 @@ namespace Microsoft.SqlTools.EditorServices /// public class Workspace { -#if false - #region Private Fields + #region Private Fields private Version SqlToolsVersion; private Dictionary workspaceFiles = new Dictionary(); @@ -93,6 +92,70 @@ namespace Microsoft.SqlTools.EditorServices return scriptFile; } + + private string ResolveFilePath(string filePath) + { + if (!IsPathInMemory(filePath)) + { + if (filePath.StartsWith(@"file://")) + { + // Client sent the path in URI format, extract the local path and trim + // any extraneous slashes + Uri fileUri = new Uri(filePath); + filePath = fileUri.LocalPath.TrimStart('/'); + } + + // Some clients send paths with UNIX-style slashes, replace those if necessary + filePath = filePath.Replace('/', '\\'); + + // Clients could specify paths with escaped space, [ and ] characters which .NET APIs + // will not handle. These paths will get appropriately escaped just before being passed + // into the SqlTools engine. + filePath = UnescapePath(filePath); + + // Get the absolute file path + filePath = Path.GetFullPath(filePath); + } + + Logger.Write(LogLevel.Verbose, "Resolved path: " + filePath); + + return filePath; + } + + internal static bool IsPathInMemory(string filePath) + { + // When viewing SqlTools files in the Git diff viewer, VS Code + // sends the contents of the file at HEAD with a URI that starts + // with 'inmemory'. Untitled files which have been marked of + // type SqlTools have a path starting with 'untitled'. + return + filePath.StartsWith("inmemory") || + filePath.StartsWith("untitled"); + } + + /// + /// Unescapes any escaped [, ] or space characters. Typically use this before calling a + /// .NET API that doesn't understand PowerShell escaped chars. + /// + /// The path to unescape. + /// The path with the ` character before [, ] and spaces removed. + public static string UnescapePath(string path) + { + if (!path.Contains("`")) + { + return path; + } + + return Regex.Replace(path, @"`(?=[ \[\]])", ""); + } + + #endregion + +#if false + + + #region Public Methods + /// /// Gets a new ScriptFile instance which is identified by the given file @@ -222,46 +285,6 @@ namespace Microsoft.SqlTools.EditorServices } } - private string ResolveFilePath(string filePath) - { - if (!IsPathInMemory(filePath)) - { - if (filePath.StartsWith(@"file://")) - { - // Client sent the path in URI format, extract the local path and trim - // any extraneous slashes - Uri fileUri = new Uri(filePath); - filePath = fileUri.LocalPath.TrimStart('/'); - } - - // Some clients send paths with UNIX-style slashes, replace those if necessary - filePath = filePath.Replace('/', '\\'); - - // Clients could specify paths with escaped space, [ and ] characters which .NET APIs - // will not handle. These paths will get appropriately escaped just before being passed - // into the SqlTools engine. - filePath = SqlToolsContext.UnescapePath(filePath); - - // Get the absolute file path - filePath = Path.GetFullPath(filePath); - } - - Logger.Write(LogLevel.Verbose, "Resolved path: " + filePath); - - return filePath; - } - - internal static bool IsPathInMemory(string filePath) - { - // When viewing SqlTools files in the Git diff viewer, VS Code - // sends the contents of the file at HEAD with a URI that starts - // with 'inmemory'. Untitled files which have been marked of - // type SqlTools have a path starting with 'untitled'. - return - filePath.StartsWith("inmemory") || - filePath.StartsWith("untitled"); - } - private string GetBaseFilePath(string filePath) { if (IsPathInMemory(filePath))