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 7386d71f..d6719141 100644
--- a/ServiceHost/Server/LanguageServer.cs
+++ b/ServiceHost/Server/LanguageServer.cs
@@ -12,6 +12,7 @@ using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Linq;
+using System;
namespace Microsoft.SqlTools.EditorServices.Protocol.Server
{
@@ -21,6 +22,8 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
public class LanguageServer : LanguageServerBase
{
private static CancellationTokenSource existingRequestCancellation;
+
+ private LanguageServerSettings currentSettings = new LanguageServerSettings();
private EditorSession editorSession;
@@ -34,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
@@ -54,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...");
@@ -67,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
@@ -133,7 +148,7 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
changedFiles.Add(changedFile);
}
- Logger.Write(LogLevel.Normal, msg.ToString());
+ Logger.Write(LogLevel.Verbose, msg.ToString());
this.RunScriptDiagnostics(
changedFiles.ToArray(),
@@ -147,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);
}
@@ -155,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);
}
@@ -171,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);
}
@@ -179,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);
}
@@ -187,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);
}
@@ -195,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);
}
@@ -203,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);
}
@@ -211,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);
}
@@ -219,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);
}
@@ -227,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);
}
@@ -235,53 +292,57 @@ 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 (!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 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)
- // {
- // // TODO: Catch a more specific exception!
- // Logger.Write(
- // LogLevel.Error,
- // string.Format(
- // "Exception while cancelling analysis task:\n\n{0}",
- // e.ToString()));
+ // 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;
- // }
+ 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.
- // TODO: Is there a better way to do this?
existingRequestCancellation = new CancellationTokenSource();
Task.Factory.StartNew(
() =>
@@ -298,7 +359,14 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
return Task.FromResult(true);
}
-
+ ///
+ /// Actually run the script diagnostics after waiting for some small delay
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
private static async Task DelayThenInvokeDiagnostics(
int delayMilliseconds,
ScriptFile[] filesToAnalyze,
@@ -329,37 +397,17 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
foreach (ScriptFile scriptFile in filesToAnalyze)
{
ScriptFileMarker[] semanticMarkers = null;
- // if (editorSession.AnalysisService != null)
- // {
- // Logger.Write(LogLevel.Verbose, "Analyzing script file: " + scriptFile.FilePath);
-
- // semanticMarkers =
- // editorSession.AnalysisService.GetSemanticMarkers(
- // scriptFile);
-
- // Logger.Write(LogLevel.Verbose, "Analysis complete.");
- // }
- // else
+ 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];
- // 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
- // }
- // };
+ semanticMarkers = new ScriptFileMarker[0];
}
await PublishScriptDiagnostics(
@@ -369,6 +417,12 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
}
}
+ ///
+ /// Send the diagnostic results back to the host application
+ ///
+ ///
+ ///
+ ///
private static async Task PublishScriptDiagnostics(
ScriptFile scriptFile,
ScriptFileMarker[] semanticMarkers,
@@ -392,6 +446,11 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
});
}
+ ///
+ /// Convert a ScriptFileMarker to a Diagnostic that is Language Service compatible
+ ///
+ ///
+ ///
private static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMarker)
{
return new Diagnostic
@@ -415,6 +474,10 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
};
}
+ ///
+ /// Map ScriptFileMarker severity to Diagnostic severity
+ ///
+ ///
private static DiagnosticSeverity MapDiagnosticSeverity(ScriptFileMarkerLevel markerLevel)
{
switch (markerLevel)
@@ -433,10 +496,14 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
}
}
+ ///
+ /// 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,
diff --git a/ServiceHost/Session/EditorSession.cs b/ServiceHost/Session/EditorSession.cs
index 310a44c8..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,80 +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.
@@ -119,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
@@ -181,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
+
}
}