diff --git a/ServiceHost/LanguageSupport/LanguageService.cs b/ServiceHost/LanguageSupport/LanguageService.cs new file mode 100644 index 00000000..3ab77697 --- /dev/null +++ b/ServiceHost/LanguageSupport/LanguageService.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.SqlTools.EditorServices; +using Microsoft.SqlTools.EditorServices.Session; + +namespace Microsoft.SqlTools.LanguageSupport +{ + /// + /// Main class for Language Service functionality + /// + public class LanguageService + { + /// + /// Gets or sets the current SQL Tools context + /// + /// + private SqlToolsContext Context { get; set; } + + /// + /// Constructor for the Language Service class + /// + /// + public LanguageService(SqlToolsContext context) + { + this.Context = context; + } + + /// + /// Gets a list of semantic diagnostic marks for the provided script file + /// + /// + public ScriptFileMarker[] GetSemanticMarkers(ScriptFile scriptFile) + { + // the commented out snippet is an example of how to create a error marker + // semanticMarkers = new ScriptFileMarker[1]; + // semanticMarkers[0] = new ScriptFileMarker() + // { + // Message = "Error message", + // Level = ScriptFileMarkerLevel.Error, + // ScriptRegion = new ScriptRegion() + // { + // File = scriptFile.FilePath, + // StartLineNumber = 2, + // StartColumnNumber = 2, + // StartOffset = 0, + // EndLineNumber = 4, + // EndColumnNumber = 10, + // EndOffset = 0 + // } + // }; + return new ScriptFileMarker[0]; + } + } +} diff --git a/ServiceHost/Program.cs b/ServiceHost/Program.cs index 622a026c..6bfd0f24 100644 --- a/ServiceHost/Program.cs +++ b/ServiceHost/Program.cs @@ -18,7 +18,9 @@ namespace Microsoft.SqlTools.ServiceHost /// static void Main(string[] args) { - Logger.Initialize(); + // turn on Verbose logging during early development + // we need to switch to Normal when preparing for public preview + Logger.Initialize(minimumLogLevel: LogLevel.Verbose); Logger.Write(LogLevel.Normal, "Starting SQL Tools Service Host"); const string hostName = "SQL Tools Service Host"; diff --git a/ServiceHost/Server/LanguageServer.cs b/ServiceHost/Server/LanguageServer.cs index 2f0f7963..d6719141 100644 --- a/ServiceHost/Server/LanguageServer.cs +++ b/ServiceHost/Server/LanguageServer.cs @@ -10,11 +10,21 @@ using System.Threading.Tasks; using Microsoft.SqlTools.EditorServices.Utility; using System.Collections.Generic; using System.Text; +using System.Threading; +using System.Linq; +using System; namespace Microsoft.SqlTools.EditorServices.Protocol.Server { + /// + /// SQL Tools VS Code Language Server request handler + /// public class LanguageServer : LanguageServerBase { + private static CancellationTokenSource existingRequestCancellation; + + private LanguageServerSettings currentSettings = new LanguageServerSettings(); + private EditorSession editorSession; /// @@ -27,6 +37,9 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server this.editorSession.StartSession(hostDetails, profilePaths); } + /// + /// Initialize the VS Code request/response callbacks + /// protected override void Initialize() { // Register all supported message types @@ -47,6 +60,9 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server this.SetRequestHandler(WorkspaceSymbolRequest.Type, this.HandleWorkspaceSymbolRequest); } + /// + /// Handles the shutdown event for the Language Server + /// protected override async Task Shutdown() { Logger.Write(LogLevel.Normal, "Language service is shutting down..."); @@ -60,14 +76,20 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server await Task.FromResult(true); } + /// + /// Handles the initialization request + /// + /// + /// + /// protected async Task HandleInitializeRequest( InitializeRequest initializeParams, RequestContext requestContext) { - Logger.Write(LogLevel.Normal, "HandleDidChangeTextDocumentNotification"); + Logger.Write(LogLevel.Verbose, "HandleDidChangeTextDocumentNotification"); // Grab the workspace path from the parameters - //editorSession.Workspace.WorkspacePath = initializeParams.RootPath; + editorSession.Workspace.WorkspacePath = initializeParams.RootPath; await requestContext.SendResult( new InitializeResult @@ -118,21 +140,20 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server ScriptFile changedFile = editorSession.Workspace.GetFile(fileUri); - // changedFile.ApplyChange( - // GetFileChangeDetails( - // textChange.Range.Value, - // textChange.Text)); + changedFile.ApplyChange( + GetFileChangeDetails( + textChange.Range.Value, + textChange.Text)); - // changedFiles.Add(changedFile); + changedFiles.Add(changedFile); } - Logger.Write(LogLevel.Normal, msg.ToString()); + Logger.Write(LogLevel.Verbose, msg.ToString()); - // // TODO: Get all recently edited files in the workspace - // this.RunScriptDiagnostics( - // changedFiles.ToArray(), - // editorSession, - // eventContext); + this.RunScriptDiagnostics( + changedFiles.ToArray(), + editorSession, + eventContext); return Task.FromResult(true); } @@ -141,7 +162,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server DidOpenTextDocumentNotification openParams, EventContext eventContext) { - Logger.Write(LogLevel.Normal, "HandleDidOpenTextDocumentNotification"); + Logger.Write(LogLevel.Verbose, "HandleDidOpenTextDocumentNotification"); return Task.FromResult(true); } @@ -149,15 +170,57 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server TextDocumentIdentifier closeParams, EventContext eventContext) { - Logger.Write(LogLevel.Normal, "HandleDidCloseTextDocumentNotification"); + Logger.Write(LogLevel.Verbose, "HandleDidCloseTextDocumentNotification"); return Task.FromResult(true); } + /// + /// Handles the configuration change event + /// + /// + /// protected async Task HandleDidChangeConfigurationNotification( DidChangeConfigurationParams configChangeParams, EventContext eventContext) { - Logger.Write(LogLevel.Normal, "HandleDidChangeConfigurationNotification"); + Logger.Write(LogLevel.Verbose, "HandleDidChangeConfigurationNotification"); + + bool oldLoadProfiles = this.currentSettings.EnableProfileLoading; + bool oldScriptAnalysisEnabled = + this.currentSettings.ScriptAnalysis.Enable.HasValue; + string oldScriptAnalysisSettingsPath = + this.currentSettings.ScriptAnalysis.SettingsPath; + + this.currentSettings.Update( + configChangeParams.Settings.SqlTools, + this.editorSession.Workspace.WorkspacePath); + + // If script analysis settings have changed we need to clear & possibly update the current diagnostic records. + if ((oldScriptAnalysisEnabled != this.currentSettings.ScriptAnalysis.Enable)) + { + // If the user just turned off script analysis or changed the settings path, send a diagnostics + // event to clear the analysis markers that they already have. + if (!this.currentSettings.ScriptAnalysis.Enable.Value) + { + ScriptFileMarker[] emptyAnalysisDiagnostics = new ScriptFileMarker[0]; + + foreach (var scriptFile in editorSession.Workspace.GetOpenedFiles()) + { + await PublishScriptDiagnostics( + scriptFile, + emptyAnalysisDiagnostics, + eventContext); + } + } + else + { + await this.RunScriptDiagnostics( + this.editorSession.Workspace.GetOpenedFiles(), + this.editorSession, + eventContext); + } + } + await Task.FromResult(true); } @@ -165,7 +228,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server TextDocumentPosition textDocumentPosition, RequestContext requestContext) { - Logger.Write(LogLevel.Normal, "HandleDefinitionRequest"); + Logger.Write(LogLevel.Verbose, "HandleDefinitionRequest"); await Task.FromResult(true); } @@ -173,7 +236,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server ReferencesParams referencesParams, RequestContext requestContext) { - Logger.Write(LogLevel.Normal, "HandleReferencesRequest"); + Logger.Write(LogLevel.Verbose, "HandleReferencesRequest"); await Task.FromResult(true); } @@ -181,7 +244,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server TextDocumentPosition textDocumentPosition, RequestContext requestContext) { - Logger.Write(LogLevel.Normal, "HandleCompletionRequest"); + Logger.Write(LogLevel.Verbose, "HandleCompletionRequest"); await Task.FromResult(true); } @@ -189,7 +252,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server CompletionItem completionItem, RequestContext requestContext) { - Logger.Write(LogLevel.Normal, "HandleCompletionResolveRequest"); + Logger.Write(LogLevel.Verbose, "HandleCompletionResolveRequest"); await Task.FromResult(true); } @@ -197,7 +260,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server TextDocumentPosition textDocumentPosition, RequestContext requestContext) { - Logger.Write(LogLevel.Normal, "HandleSignatureHelpRequest"); + Logger.Write(LogLevel.Verbose, "HandleSignatureHelpRequest"); await Task.FromResult(true); } @@ -205,7 +268,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server TextDocumentPosition textDocumentPosition, RequestContext requestContext) { - Logger.Write(LogLevel.Normal, "HandleDocumentHighlightRequest"); + Logger.Write(LogLevel.Verbose, "HandleDocumentHighlightRequest"); await Task.FromResult(true); } @@ -213,7 +276,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server TextDocumentPosition textDocumentPosition, RequestContext requestContext) { - Logger.Write(LogLevel.Normal, "HandleHoverRequest"); + Logger.Write(LogLevel.Verbose, "HandleHoverRequest"); await Task.FromResult(true); } @@ -221,7 +284,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server TextDocumentIdentifier textDocumentIdentifier, RequestContext requestContext) { - Logger.Write(LogLevel.Normal, "HandleDocumentSymbolRequest"); + Logger.Write(LogLevel.Verbose, "HandleDocumentSymbolRequest"); await Task.FromResult(true); } @@ -229,8 +292,226 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server WorkspaceSymbolParams workspaceSymbolParams, RequestContext requestContext) { - Logger.Write(LogLevel.Normal, "HandleWorkspaceSymbolRequest"); + Logger.Write(LogLevel.Verbose, "HandleWorkspaceSymbolRequest"); await Task.FromResult(true); - } + } + + /// + /// Runs script diagnostics on changed files + /// + /// + /// + /// + private Task RunScriptDiagnostics( + ScriptFile[] filesToAnalyze, + EditorSession editorSession, + EventContext eventContext) + { + if (!this.currentSettings.ScriptAnalysis.Enable.Value) + { + // If the user has disabled script analysis, skip it entirely + return Task.FromResult(true); + } + + // If there's an existing task, attempt to cancel it + try + { + if (existingRequestCancellation != null) + { + // Try to cancel the request + existingRequestCancellation.Cancel(); + + // If cancellation didn't throw an exception, + // clean up the existing token + existingRequestCancellation.Dispose(); + existingRequestCancellation = null; + } + } + catch (Exception e) + { + Logger.Write( + LogLevel.Error, + string.Format( + "Exception while cancelling analysis task:\n\n{0}", + e.ToString())); + + TaskCompletionSource cancelTask = new TaskCompletionSource(); + cancelTask.SetCanceled(); + return cancelTask.Task; + } + + // Create a fresh cancellation token and then start the task. + // We create this on a different TaskScheduler so that we + // don't block the main message loop thread. + existingRequestCancellation = new CancellationTokenSource(); + Task.Factory.StartNew( + () => + DelayThenInvokeDiagnostics( + 750, + filesToAnalyze, + editorSession, + eventContext, + existingRequestCancellation.Token), + CancellationToken.None, + TaskCreationOptions.None, + TaskScheduler.Default); + + return Task.FromResult(true); + } + + /// + /// Actually run the script diagnostics after waiting for some small delay + /// + /// + /// + /// + /// + /// + private static async Task DelayThenInvokeDiagnostics( + int delayMilliseconds, + ScriptFile[] filesToAnalyze, + EditorSession editorSession, + EventContext eventContext, + CancellationToken cancellationToken) + { + // First of all, wait for the desired delay period before + // analyzing the provided list of files + try + { + await Task.Delay(delayMilliseconds, cancellationToken); + } + catch (TaskCanceledException) + { + // If the task is cancelled, exit directly + return; + } + + // If we've made it past the delay period then we don't care + // about the cancellation token anymore. This could happen + // when the user stops typing for long enough that the delay + // period ends but then starts typing while analysis is going + // on. It makes sense to send back the results from the first + // delay period while the second one is ticking away. + + // Get the requested files + foreach (ScriptFile scriptFile in filesToAnalyze) + { + ScriptFileMarker[] semanticMarkers = null; + if (editorSession.LanguageService != null) + { + Logger.Write(LogLevel.Verbose, "Analyzing script file: " + scriptFile.FilePath); + semanticMarkers = editorSession.LanguageService.GetSemanticMarkers(scriptFile); + Logger.Write(LogLevel.Verbose, "Analysis complete."); + } + else + { + // Semantic markers aren't available if the AnalysisService + // isn't available + semanticMarkers = new ScriptFileMarker[0]; + } + + await PublishScriptDiagnostics( + scriptFile, + semanticMarkers, + eventContext); + } + } + + /// + /// Send the diagnostic results back to the host application + /// + /// + /// + /// + private static async Task PublishScriptDiagnostics( + ScriptFile scriptFile, + ScriptFileMarker[] semanticMarkers, + EventContext eventContext) + { + var allMarkers = scriptFile.SyntaxMarkers != null + ? scriptFile.SyntaxMarkers.Concat(semanticMarkers) + : semanticMarkers; + + // Always send syntax and semantic errors. We want to + // make sure no out-of-date markers are being displayed. + await eventContext.SendEvent( + PublishDiagnosticsNotification.Type, + new PublishDiagnosticsNotification + { + Uri = scriptFile.ClientFilePath, + Diagnostics = + allMarkers + .Select(GetDiagnosticFromMarker) + .ToArray() + }); + } + + /// + /// Convert a ScriptFileMarker to a Diagnostic that is Language Service compatible + /// + /// + /// + private static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMarker) + { + return new Diagnostic + { + Severity = MapDiagnosticSeverity(scriptFileMarker.Level), + Message = scriptFileMarker.Message, + Range = new Range + { + // TODO: What offsets should I use? + Start = new Position + { + Line = scriptFileMarker.ScriptRegion.StartLineNumber - 1, + Character = scriptFileMarker.ScriptRegion.StartColumnNumber - 1 + }, + End = new Position + { + Line = scriptFileMarker.ScriptRegion.EndLineNumber - 1, + Character = scriptFileMarker.ScriptRegion.EndColumnNumber - 1 + } + } + }; + } + + /// + /// Map ScriptFileMarker severity to Diagnostic severity + /// + /// + private static DiagnosticSeverity MapDiagnosticSeverity(ScriptFileMarkerLevel markerLevel) + { + switch (markerLevel) + { + case ScriptFileMarkerLevel.Error: + return DiagnosticSeverity.Error; + + case ScriptFileMarkerLevel.Warning: + return DiagnosticSeverity.Warning; + + case ScriptFileMarkerLevel.Information: + return DiagnosticSeverity.Information; + + default: + return DiagnosticSeverity.Error; + } + } + + /// + /// Switch from 0-based offsets to 1 based offsets + /// + /// + /// + private static FileChange GetFileChangeDetails(Range changeRange, string insertString) + { + // The protocol's positions are zero-based so add 1 to all offsets + return new FileChange + { + InsertString = insertString, + Line = changeRange.Start.Line + 1, + Offset = changeRange.Start.Character + 1, + EndLine = changeRange.End.Line + 1, + EndOffset = changeRange.End.Character + 1 + }; + } } } diff --git a/ServiceHost/Session/EditorSession.cs b/ServiceHost/Session/EditorSession.cs index 4b6c9a49..3c592a8f 100644 --- a/ServiceHost/Session/EditorSession.cs +++ b/ServiceHost/Session/EditorSession.cs @@ -5,6 +5,7 @@ using System; using Microsoft.SqlTools.EditorServices.Session; +using Microsoft.SqlTools.LanguageSupport; namespace Microsoft.SqlTools.EditorServices { @@ -21,6 +22,12 @@ namespace Microsoft.SqlTools.EditorServices /// public Workspace Workspace { get; private set; } + /// + /// Gets or sets the Language Service + /// + /// + public LanguageService LanguageService { get; set; } + /// /// Gets the SqlToolsContext instance for this session. /// @@ -30,84 +37,6 @@ namespace Microsoft.SqlTools.EditorServices #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. - /// - public LanguageService LanguageService { get; private set; } - - /// - /// Gets the AnalysisService instance for this session. - /// - public AnalysisService AnalysisService { get; private set; } - - /// - /// Gets the DebugService instance for this session. - /// - public DebugService DebugService { get; private set; } - - /// - /// Gets the ConsoleService instance for this session. - /// - public ConsoleService ConsoleService { get; private set; } - - /// - /// Gets the ExtensionService instance for this session. - /// - public ExtensionService ExtensionService { get; private set; } - - #endregion - - #region Public Methods - /// /// Starts the session using the provided IConsoleHost implementation /// for the ConsoleService. @@ -123,59 +52,11 @@ namespace Microsoft.SqlTools.EditorServices // 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); } - /// - /// Restarts the AnalysisService so it can be configured with a new settings file. - /// - /// Path to the settings file. - public void RestartAnalysisService(string settingsPath) - { - this.AnalysisService?.Dispose(); - InstantiateAnalysisService(settingsPath); - } - - internal void InstantiateAnalysisService(string settingsPath = null) - { - // Only enable the AnalysisService if the machine has SqlTools - // v5 installed. Script Analyzer works on earlier SqlTools - // versions but our hard dependency on their binaries complicates - // the deployment and assembly loading since we would have to - // conditionally load the binaries for v3/v4 support. This problem - // will be solved in the future by using Script Analyzer as a - // module rather than an assembly dependency. - if (this.SqlToolsContext.SqlToolsVersion.Major >= 5) - { - // AnalysisService will throw FileNotFoundException if - // Script Analyzer binaries are not included. - try - { - this.AnalysisService = new AnalysisService(this.SqlToolsContext.ConsoleHost, settingsPath); - } - catch (FileNotFoundException) - { - Logger.Write( - LogLevel.Warning, - "Script Analyzer binaries not found, AnalysisService will be disabled."); - } - } - else - { - Logger.Write( - LogLevel.Normal, - "Script Analyzer cannot be loaded due to unsupported SqlTools version " + - this.SqlToolsContext.SqlToolsVersion.ToString()); - } - } - #endregion #region IDisposable Implementation @@ -185,21 +66,10 @@ namespace Microsoft.SqlTools.EditorServices /// services used in this session. /// public void Dispose() - { - if (this.AnalysisService != null) - { - this.AnalysisService.Dispose(); - this.AnalysisService = null; - } - - if (this.SqlToolsContext != null) - { - this.SqlToolsContext.Dispose(); - this.SqlToolsContext = null; - } + { } #endregion -#endif + } } diff --git a/ServiceHost/Workspace/BufferPosition.cs b/ServiceHost/Workspace/BufferPosition.cs index 0a892f79..8f790d85 100644 --- a/ServiceHost/Workspace/BufferPosition.cs +++ b/ServiceHost/Workspace/BufferPosition.cs @@ -108,4 +108,3 @@ namespace Microsoft.SqlTools.EditorServices #endregion } } - diff --git a/ServiceHost/Workspace/FilePosition.cs b/ServiceHost/Workspace/FilePosition.cs index 89d915ee..2cb58745 100644 --- a/ServiceHost/Workspace/FilePosition.cs +++ b/ServiceHost/Workspace/FilePosition.cs @@ -11,14 +11,6 @@ namespace Microsoft.SqlTools.EditorServices /// public class FilePosition : BufferPosition { - public FilePosition( - ScriptFile scriptFile, - int line, - int column) - : base(line, column) - { - } -#if false #region Private Fields private ScriptFile scriptFile; @@ -112,7 +104,7 @@ namespace Microsoft.SqlTools.EditorServices } #endregion -#endif + } } diff --git a/ServiceHost/Workspace/ScriptFile.cs b/ServiceHost/Workspace/ScriptFile.cs index 7b041832..90d66244 100644 --- a/ServiceHost/Workspace/ScriptFile.cs +++ b/ServiceHost/Workspace/ScriptFile.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -//using System.Management.Automation; -//using System.Management.Automation.Language; namespace Microsoft.SqlTools.EditorServices { @@ -18,34 +16,8 @@ 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 - private Token[] scriptTokens; private Version SqlToolsVersion; #endregion @@ -121,23 +93,6 @@ namespace Microsoft.SqlTools.EditorServices private set; } - /// - /// Gets the ScriptBlockAst representing the parsed script contents. - /// - public ScriptBlockAst ScriptAst - { - get; - private set; - } - - /// - /// Gets the array of Tokens representing the parsed script contents. - /// - public Token[] ScriptTokens - { - get { return this.scriptTokens; } - } - /// /// Gets the array of filepaths dot sourced in this ScriptFile /// @@ -502,6 +457,7 @@ namespace Microsoft.SqlTools.EditorServices /// private void ParseFileContents() { +#if false ParseError[] parseErrors = null; // First, get the updated file range @@ -574,9 +530,9 @@ namespace Microsoft.SqlTools.EditorServices //Get all dot sourced referenced files and store them this.ReferencedFiles = AstOperations.FindDotSourcedIncludes(this.ScriptAst); +#endif } #endregion -#endif } } diff --git a/ServiceHost/Workspace/ScriptFileMarker.cs b/ServiceHost/Workspace/ScriptFileMarker.cs index fbe944d6..87c2576c 100644 --- a/ServiceHost/Workspace/ScriptFileMarker.cs +++ b/ServiceHost/Workspace/ScriptFileMarker.cs @@ -3,14 +3,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -using Microsoft.SqlTools.EditorServices.Utility; -using System; -//using System.Management.Automation.Language; - -#if ScriptAnalyzer -using Microsoft.Windows.SqlTools.ScriptAnalyzer.Generic; -#endif - namespace Microsoft.SqlTools.EditorServices { /// @@ -59,60 +51,6 @@ namespace Microsoft.SqlTools.EditorServices public ScriptRegion ScriptRegion { get; set; } #endregion - - #region Public Methods - -#if false - internal static ScriptFileMarker FromParseError( - ParseError parseError) - { - Validate.IsNotNull("parseError", parseError); - - return new ScriptFileMarker - { - Message = parseError.Message, - Level = ScriptFileMarkerLevel.Error, - ScriptRegion = ScriptRegion.Create(parseError.Extent) - }; - } -#endif - -#if ScriptAnalyzer - internal static ScriptFileMarker FromDiagnosticRecord( - DiagnosticRecord diagnosticRecord) - { - Validate.IsNotNull("diagnosticRecord", diagnosticRecord); - - return new ScriptFileMarker - { - Message = diagnosticRecord.Message, - Level = GetMarkerLevelFromDiagnosticSeverity(diagnosticRecord.Severity), - ScriptRegion = ScriptRegion.Create(diagnosticRecord.Extent) - }; - } - - private static ScriptFileMarkerLevel GetMarkerLevelFromDiagnosticSeverity( - DiagnosticSeverity diagnosticSeverity) - { - switch (diagnosticSeverity) - { - case DiagnosticSeverity.Information: - return ScriptFileMarkerLevel.Information; - case DiagnosticSeverity.Warning: - return ScriptFileMarkerLevel.Warning; - case DiagnosticSeverity.Error: - return ScriptFileMarkerLevel.Error; - default: - throw new ArgumentException( - string.Format( - "The provided DiagnosticSeverity value '{0}' is unknown.", - diagnosticSeverity), - "diagnosticSeverity"); - } - } -#endif - - #endregion } } diff --git a/ServiceHost/Workspace/Workspace.cs b/ServiceHost/Workspace/Workspace.cs index 6188a806..39e1d70f 100644 --- a/ServiceHost/Workspace/Workspace.cs +++ b/ServiceHost/Workspace/Workspace.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; +using System.Linq; namespace Microsoft.SqlTools.EditorServices { @@ -149,15 +150,7 @@ namespace Microsoft.SqlTools.EditorServices return Regex.Replace(path, @"`(?=[ \[\]])", ""); } - #endregion - -#if false - - - #region Public Methods - - - /// + /// /// Gets a new ScriptFile instance which is identified by the given file /// path and initially contains the given buffer contents. /// @@ -211,80 +204,6 @@ namespace Microsoft.SqlTools.EditorServices this.workspaceFiles.Remove(scriptFile.Id); } - /// - /// Gets all file references by recursively searching - /// through referenced files in a scriptfile - /// - /// Contains the details and contents of an open script file - /// A scriptfile array where the first file - /// in the array is the "root file" of the search - public ScriptFile[] ExpandScriptReferences(ScriptFile scriptFile) - { - Dictionary referencedScriptFiles = new Dictionary(); - List expandedReferences = new List(); - - // add original file so it's not searched for, then find all file references - referencedScriptFiles.Add(scriptFile.Id, scriptFile); - RecursivelyFindReferences(scriptFile, referencedScriptFiles); - - // remove original file from referened file and add it as the first element of the - // expanded referenced list to maintain order so the original file is always first in the list - referencedScriptFiles.Remove(scriptFile.Id); - expandedReferences.Add(scriptFile); - - if (referencedScriptFiles.Count > 0) - { - expandedReferences.AddRange(referencedScriptFiles.Values); - } - - return expandedReferences.ToArray(); - } - - #endregion - - #region Private Methods - - /// - /// Recusrively searches through referencedFiles in scriptFiles - /// and builds a Dictonary of the file references - /// - /// Details an contents of "root" script file - /// A Dictionary of referenced script files - private void RecursivelyFindReferences( - ScriptFile scriptFile, - Dictionary referencedScriptFiles) - { - // Get the base path of the current script for use in resolving relative paths - string baseFilePath = - GetBaseFilePath( - scriptFile.FilePath); - - ScriptFile referencedFile; - foreach (string referencedFileName in scriptFile.ReferencedFiles) - { - string resolvedScriptPath = - this.ResolveRelativeScriptPath( - baseFilePath, - referencedFileName); - - // Make sure file exists before trying to get the file - if (File.Exists(resolvedScriptPath)) - { - // Get the referenced file if it's not already in referencedScriptFiles - referencedFile = this.GetFile(resolvedScriptPath); - - // Normalize the resolved script path and add it to the - // referenced files list if it isn't there already - resolvedScriptPath = resolvedScriptPath.ToLower(); - if (!referencedScriptFiles.ContainsKey(resolvedScriptPath)) - { - referencedScriptFiles.Add(resolvedScriptPath, referencedFile); - RecursivelyFindReferences(referencedFile, referencedScriptFiles); - } - } - } - } - private string GetBaseFilePath(string filePath) { if (IsPathInMemory(filePath)) @@ -324,7 +243,6 @@ namespace Microsoft.SqlTools.EditorServices return combinedPath; } - #endregion -#endif + #endregion } }