diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/LanguageFlavorChange.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/LanguageFlavorChange.cs
new file mode 100644
index 00000000..bab9c206
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/LanguageFlavorChange.cs
@@ -0,0 +1,42 @@
+//
+// 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.Hosting.Protocol.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
+{
+
+ ///
+ /// Parameters for the Language Flavor Change notification.
+ ///
+ public class LanguageFlavorChangeParams
+ {
+ ///
+ /// A URI identifying the affected resource
+ ///
+ public string Uri { get; set; }
+
+ ///
+ /// The primary language
+ ///
+ public string Language { get; set; }
+
+ ///
+ /// The specific language flavor that is being set
+ ///
+ public string Flavor { get; set; }
+ }
+
+ ///
+ /// Defines an event that is sent from the client to notify that
+ /// the client is exiting and the server should as well.
+ ///
+ public class LanguageFlavorChangeNotification
+ {
+ public static readonly
+ EventType Type =
+ EventType.Create("connection/languageflavorchanged");
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
index b702e4cd..24d7fc55 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteHelper.cs
@@ -26,10 +26,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
public static class AutoCompleteHelper
{
- private const int PrepopulateBindTimeout = 60000;
-
- private static WorkspaceService workspaceServiceInstance;
-
private static CompletionItem[] emptyCompletionList = new CompletionItem[0];
private static readonly string[] DefaultCompletionText = new string[]
@@ -354,26 +350,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
return pos > -1;
}
- ///
- /// Gets or sets the current workspace service instance
- /// Setter for internal testing purposes only
- ///
- internal static WorkspaceService WorkspaceServiceInstance
- {
- get
- {
- if (AutoCompleteHelper.workspaceServiceInstance == null)
- {
- AutoCompleteHelper.workspaceServiceInstance = WorkspaceService.Instance;
- }
- return AutoCompleteHelper.workspaceServiceInstance;
- }
- set
- {
- AutoCompleteHelper.workspaceServiceInstance = value;
- }
- }
-
///
/// Get the default completion list from hard-coded list
///
@@ -491,93 +467,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
return completions.ToArray();
}
- ///
- /// Preinitialize the parser and binder with common metadata.
- /// This should front load the long binding wait to the time the
- /// connection is established. Once this is completed other binding
- /// requests should be faster.
- ///
- ///
- ///
- internal static void PrepopulateCommonMetadata(
- ConnectionInfo info,
- ScriptParseInfo scriptInfo,
- ConnectedBindingQueue bindingQueue)
- {
- if (scriptInfo.IsConnected)
- {
- var scriptFile = AutoCompleteHelper.WorkspaceServiceInstance.Workspace.GetFile(info.OwnerUri);
- if (scriptFile == null)
- {
- return;
- }
-
- LanguageService.Instance.ParseAndBind(scriptFile, info);
-
- if (Monitor.TryEnter(scriptInfo.BuildingMetadataLock, LanguageService.OnConnectionWaitTimeout))
- {
- try
- {
- QueueItem queueItem = bindingQueue.QueueBindingOperation(
- key: scriptInfo.ConnectionKey,
- bindingTimeout: AutoCompleteHelper.PrepopulateBindTimeout,
- waitForLockTimeout: AutoCompleteHelper.PrepopulateBindTimeout,
- bindOperation: (bindingContext, cancelToken) =>
- {
- // parse a simple statement that returns common metadata
- ParseResult parseResult = Parser.Parse(
- "select ",
- bindingContext.ParseOptions);
-
- List parseResults = new List();
- parseResults.Add(parseResult);
- bindingContext.Binder.Bind(
- parseResults,
- info.ConnectionDetails.DatabaseName,
- BindMode.Batch);
-
- // get the completion list from SQL Parser
- var suggestions = Resolver.FindCompletions(
- parseResult, 1, 8,
- bindingContext.MetadataDisplayInfoProvider);
-
- // this forces lazy evaluation of the suggestion metadata
- AutoCompleteHelper.ConvertDeclarationsToCompletionItems(suggestions, 1, 8, 8);
-
- parseResult = Parser.Parse(
- "exec ",
- bindingContext.ParseOptions);
-
- parseResults = new List();
- parseResults.Add(parseResult);
- bindingContext.Binder.Bind(
- parseResults,
- info.ConnectionDetails.DatabaseName,
- BindMode.Batch);
-
- // get the completion list from SQL Parser
- suggestions = Resolver.FindCompletions(
- parseResult, 1, 6,
- bindingContext.MetadataDisplayInfoProvider);
-
- // this forces lazy evaluation of the suggestion metadata
- AutoCompleteHelper.ConvertDeclarationsToCompletionItems(suggestions, 1, 6, 6);
- return null;
- });
-
- queueItem.ItemProcessed.WaitOne();
- }
- catch
- {
- }
- finally
- {
- Monitor.Exit(scriptInfo.BuildingMetadataLock);
- }
- }
- }
- }
-
///
/// Converts a SQL Parser QuickInfo object into a VS Code Hover object
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/DiagnosticsHelper.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/DiagnosticsHelper.cs
index 41de8b55..0ae47a03 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/DiagnosticsHelper.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/DiagnosticsHelper.cs
@@ -3,11 +3,14 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
+using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
+using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{
@@ -46,6 +49,29 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
});
}
+ ///
+ /// Send the diagnostic results back to the host application
+ ///
+ ///
+ ///
+ ///
+ internal static async Task ClearScriptDiagnostics(
+ string uri,
+ EventContext eventContext)
+ {
+ Validate.IsNotNullOrEmptyString(nameof(uri), uri);
+ Validate.IsNotNull(nameof(eventContext), eventContext);
+ // 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 = uri,
+ Diagnostics = Array.Empty()
+ });
+ }
+
///
/// Convert a ScriptFileMarker to a Diagnostic that is Language Service compatible
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
index 44467a30..3ee1f4c5 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
@@ -4,6 +4,7 @@
//
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -36,6 +37,24 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
public sealed class LanguageService
{
+ #region Singleton Instance Implementation
+
+ private static readonly Lazy instance = new Lazy(() => new LanguageService());
+
+ ///
+ /// Gets the singleton instance object
+ ///
+ public static LanguageService Instance
+ {
+ get { return instance.Value; }
+ }
+
+ #endregion
+
+ #region Private / internal instance fields and constructor
+ private const int PrepopulateBindTimeout = 60000;
+
+ public const string SQL_LANG = "SQL";
private const int OneSecond = 1000;
internal const string DefaultBatchSeperator = "GO";
@@ -50,9 +69,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
internal const int PeekDefinitionTimeout = 10 * OneSecond;
- private static ConnectionService connectionService = null;
+ private ConnectionService connectionService = null;
- private static WorkspaceService workspaceServiceInstance;
+ private WorkspaceService workspaceServiceInstance;
private object parseMapLock = new object();
@@ -60,51 +79,13 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
private ConnectedBindingQueue bindingQueue = new ConnectedBindingQueue();
- private ParseOptions defaultParseOptions = new ParseOptions(
+ private ParseOptions defaultParseOptions = new ParseOptions(
batchSeparator: LanguageService.DefaultBatchSeperator,
isQuotedIdentifierSet: true,
compatibilityLevel: DatabaseCompatibilityLevel.Current,
transactSqlVersion: TransactSqlVersion.Current);
- ///
- /// Gets or sets the binding queue instance
- /// Internal for testing purposes only
- ///
- internal ConnectedBindingQueue BindingQueue
- {
- get
- {
- return this.bindingQueue;
- }
- set
- {
- this.bindingQueue = value;
- }
- }
-
- ///
- /// Internal for testing purposes only
- ///
- internal static ConnectionService ConnectionServiceInstance
- {
- get
- {
- if (connectionService == null)
- {
- connectionService = ConnectionService.Instance;
- }
- return connectionService;
- }
-
- set
- {
- connectionService = value;
- }
- }
-
- #region Singleton Instance Implementation
-
- private static readonly Lazy instance = new Lazy(() => new LanguageService());
+ private ConcurrentDictionary nonMssqlUriMap = new ConcurrentDictionary();
private Lazy> scriptParseInfoMap
= new Lazy>(() => new Dictionary());
@@ -120,14 +101,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
}
- ///
- /// Gets the singleton instance object
- ///
- public static LanguageService Instance
- {
- get { return instance.Value; }
- }
-
private ParseOptions DefaultParseOptions
{
get
@@ -147,42 +120,78 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
#region Properties
- private static CancellationTokenSource ExistingRequestCancellation { get; set; }
+ ///
+ /// Gets or sets the binding queue instance
+ /// Internal for testing purposes only
+ ///
+ internal ConnectedBindingQueue BindingQueue
+ {
+ get
+ {
+ return this.bindingQueue;
+ }
+ set
+ {
+ this.bindingQueue = value;
+ }
+ }
///
- /// Gets the current settings
+ /// Internal for testing purposes only
///
- internal SqlToolsSettings CurrentSettings
+ internal ConnectionService ConnectionServiceInstance
{
- get { return WorkspaceService.Instance.CurrentSettings; }
+ get
+ {
+ if (connectionService == null)
+ {
+ connectionService = ConnectionService.Instance;
+ }
+ return connectionService;
+ }
+
+ set
+ {
+ connectionService = value;
+ }
}
+ private CancellationTokenSource existingRequestCancellation;
+
///
/// Gets or sets the current workspace service instance
/// Setter for internal testing purposes only
///
- internal static WorkspaceService WorkspaceServiceInstance
+ internal WorkspaceService WorkspaceServiceInstance
{
get
{
- if (LanguageService.workspaceServiceInstance == null)
+ if (workspaceServiceInstance == null)
{
- LanguageService.workspaceServiceInstance = WorkspaceService.Instance;
+ workspaceServiceInstance = WorkspaceService.Instance;
}
- return LanguageService.workspaceServiceInstance;
+ return workspaceServiceInstance;
}
set
{
- LanguageService.workspaceServiceInstance = value;
+ workspaceServiceInstance = value;
}
}
+ ///
+ /// Gets the current settings
+ ///
+ internal SqlToolsSettings CurrentWorkspaceSettings
+ {
+ get { return WorkspaceServiceInstance.CurrentSettings; }
+ }
+
///
/// Gets the current workspace instance
///
internal Workspace.Workspace CurrentWorkspace
{
- get { return LanguageService.WorkspaceServiceInstance.Workspace; }
+ get { return WorkspaceServiceInstance.Workspace; }
}
///
@@ -214,6 +223,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
serviceHost.SetRequestHandler(DefinitionRequest.Type, HandleDefinitionRequest);
serviceHost.SetEventHandler(RebuildIntelliSenseNotification.Type, HandleRebuildIntelliSenseNotification);
+ serviceHost.SetEventHandler(LanguageFlavorChangeNotification.Type, HandleDidChangeLanguageFlavorNotification);
// Register a no-op shutdown task for validation of the shutdown logic
serviceHost.RegisterShutdownTask(async (shutdownParams, shutdownRequestContext) =>
@@ -224,13 +234,13 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
});
// Register the configuration update handler
- WorkspaceService.Instance.RegisterConfigChangeCallback(HandleDidChangeConfigurationNotification);
+ WorkspaceServiceInstance.RegisterConfigChangeCallback(HandleDidChangeConfigurationNotification);
// Register the file change update handler
- WorkspaceService.Instance.RegisterTextDocChangeCallback(HandleDidChangeTextDocumentNotification);
+ WorkspaceServiceInstance.RegisterTextDocChangeCallback(HandleDidChangeTextDocumentNotification);
// Register the file open update handler
- WorkspaceService.Instance.RegisterTextDocOpenCallback(HandleDidOpenTextDocumentNotification);
+ WorkspaceServiceInstance.RegisterTextDocOpenCallback(HandleDidOpenTextDocumentNotification);
// Register a callback for when a connection is created
ConnectionServiceInstance.RegisterOnConnectionTask(UpdateLanguageServiceOnConnection);
@@ -253,23 +263,23 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
///
///
- internal static async Task HandleCompletionRequest(
+ internal async Task HandleCompletionRequest(
TextDocumentPosition textDocumentPosition,
RequestContext requestContext)
{
// check if Intellisense suggestions are enabled
- if (!WorkspaceService.Instance.CurrentSettings.IsSuggestionsEnabled)
+ if (ShouldSkipIntellisense(textDocumentPosition.TextDocument.Uri))
{
await Task.FromResult(true);
}
else
{
// get the current list of completion items and return to client
- var scriptFile = LanguageService.WorkspaceServiceInstance.Workspace.GetFile(
+ var scriptFile = CurrentWorkspace.GetFile(
textDocumentPosition.TextDocument.Uri);
ConnectionInfo connInfo;
- LanguageService.ConnectionServiceInstance.TryFindConnection(
+ ConnectionServiceInstance.TryFindConnection(
scriptFile.ClientFilePath,
out connInfo);
@@ -287,34 +297,35 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
///
///
- internal static async Task HandleCompletionResolveRequest(
+ internal async Task HandleCompletionResolveRequest(
CompletionItem completionItem,
RequestContext requestContext)
{
// check if Intellisense suggestions are enabled
- if (!WorkspaceService.Instance.CurrentSettings.IsSuggestionsEnabled)
+ // Note: Do not know file, so no need to check for MSSQL flavor
+ if (!CurrentWorkspaceSettings.IsSuggestionsEnabled)
{
await Task.FromResult(true);
}
else
{
- completionItem = LanguageService.Instance.ResolveCompletionItem(completionItem);
+ completionItem = ResolveCompletionItem(completionItem);
await requestContext.SendResult(completionItem);
}
}
- internal static async Task HandleDefinitionRequest(TextDocumentPosition textDocumentPosition, RequestContext requestContext)
+ internal async Task HandleDefinitionRequest(TextDocumentPosition textDocumentPosition, RequestContext requestContext)
{
DocumentStatusHelper.SendStatusChange(requestContext, textDocumentPosition, DocumentStatusHelper.DefinitionRequested);
- if (WorkspaceService.Instance.CurrentSettings.IsIntelliSenseEnabled)
+ if (!ShouldSkipIntellisense(textDocumentPosition.TextDocument.Uri))
{
// Retrieve document and connection
ConnectionInfo connInfo;
- var scriptFile = LanguageService.WorkspaceServiceInstance.Workspace.GetFile(textDocumentPosition.TextDocument.Uri);
- bool isConnected = LanguageService.ConnectionServiceInstance.TryFindConnection(scriptFile.ClientFilePath, out connInfo);
+ var scriptFile = CurrentWorkspace.GetFile(textDocumentPosition.TextDocument.Uri);
+ bool isConnected = ConnectionServiceInstance.TryFindConnection(scriptFile.ClientFilePath, out connInfo);
bool succeeded = false;
- DefinitionResult definitionResult = LanguageService.Instance.GetDefinition(textDocumentPosition, scriptFile, connInfo);
+ DefinitionResult definitionResult = GetDefinition(textDocumentPosition, scriptFile, connInfo);
if (definitionResult != null)
{
if (definitionResult.IsErrorResult)
@@ -327,6 +338,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
succeeded = true;
}
}
+ else
+ {
+ // Send an empty result so that processing does not hang
+ await requestContext.SendResult(Array.Empty());
+ }
DocumentStatusHelper.SendTelemetryEvent(requestContext, CreatePeekTelemetryProps(succeeded, isConnected));
}
@@ -349,14 +365,14 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// turn off this code until needed (10/28/2016)
#if false
- private static async Task HandleReferencesRequest(
+ private async Task HandleReferencesRequest(
ReferencesParams referencesParams,
RequestContext requestContext)
{
await Task.FromResult(true);
}
- private static async Task HandleDocumentHighlightRequest(
+ private async Task HandleDocumentHighlightRequest(
TextDocumentPosition textDocumentPosition,
RequestContext requestContext)
{
@@ -364,21 +380,21 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
#endif
- internal static async Task HandleSignatureHelpRequest(
+ internal async Task HandleSignatureHelpRequest(
TextDocumentPosition textDocumentPosition,
RequestContext requestContext)
{
// check if Intellisense suggestions are enabled
- if (!WorkspaceService.Instance.CurrentSettings.IsSuggestionsEnabled)
+ if (ShouldSkipNonMssqlFile(textDocumentPosition))
{
await Task.FromResult(true);
}
else
{
- ScriptFile scriptFile = WorkspaceService.Instance.Workspace.GetFile(
+ ScriptFile scriptFile = CurrentWorkspace.GetFile(
textDocumentPosition.TextDocument.Uri);
- SignatureHelp help = LanguageService.Instance.GetSignatureHelp(textDocumentPosition, scriptFile);
+ SignatureHelp help = GetSignatureHelp(textDocumentPosition, scriptFile);
if (help != null)
{
await requestContext.SendResult(help);
@@ -390,17 +406,18 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
}
- private static async Task HandleHoverRequest(
+ private async Task HandleHoverRequest(
TextDocumentPosition textDocumentPosition,
RequestContext requestContext)
{
// check if Quick Info hover tooltips are enabled
- if (WorkspaceService.Instance.CurrentSettings.IsQuickInfoEnabled)
+ if (CurrentWorkspaceSettings.IsQuickInfoEnabled
+ && !ShouldSkipNonMssqlFile(textDocumentPosition))
{
- var scriptFile = WorkspaceService.Instance.Workspace.GetFile(
+ var scriptFile = CurrentWorkspace.GetFile(
textDocumentPosition.TextDocument.Uri);
- var hover = LanguageService.Instance.GetHoverItem(textDocumentPosition, scriptFile);
+ var hover = GetHoverItem(textDocumentPosition, scriptFile);
if (hover != null)
{
await requestContext.SendResult(hover);
@@ -426,7 +443,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{
// if not in the preview window and diagnostics are enabled then run diagnostics
if (!IsPreviewWindow(scriptFile)
- && WorkspaceService.Instance.CurrentSettings.IsDiagnositicsEnabled)
+ && CurrentWorkspaceSettings.IsDiagnosticsEnabled)
{
await RunScriptDiagnostics(
new ScriptFile[] { scriptFile },
@@ -443,8 +460,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
public async Task HandleDidChangeTextDocumentNotification(ScriptFile[] changedFiles, EventContext eventContext)
{
- if (WorkspaceService.Instance.CurrentSettings.IsDiagnositicsEnabled)
+ if (CurrentWorkspaceSettings.IsDiagnosticsEnabled)
{
+ // Only process files that are MSSQL flavor
await this.RunScriptDiagnostics(
changedFiles.ToArray(),
eventContext);
@@ -472,7 +490,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
ConnectionInfo connInfo;
- LanguageService.ConnectionServiceInstance.TryFindConnection(
+ ConnectionServiceInstance.TryFindConnection(
scriptFile.ClientFilePath,
out connInfo);
@@ -502,7 +520,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// if not in the preview window and diagnostics are enabled then run diagnostics
if (!IsPreviewWindow(scriptFile)
- && WorkspaceService.Instance.CurrentSettings.IsDiagnositicsEnabled)
+ && CurrentWorkspaceSettings.IsDiagnosticsEnabled)
{
RunScriptDiagnostics(
new ScriptFile[] { scriptFile },
@@ -541,20 +559,20 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
bool? oldEnableDiagnostics = oldSettings.SqlTools.IntelliSense.EnableErrorChecking;
// update the current settings to reflect any changes
- CurrentSettings.Update(newSettings);
+ CurrentWorkspaceSettings.Update(newSettings);
// if script analysis settings have changed we need to clear the current diagnostic markers
if (oldEnableIntelliSense != newSettings.SqlTools.IntelliSense.EnableIntellisense
|| oldEnableDiagnostics != newSettings.SqlTools.IntelliSense.EnableErrorChecking)
{
// if the user just turned off diagnostics then send an event to clear the error markers
- if (!newSettings.IsDiagnositicsEnabled)
+ if (!newSettings.IsDiagnosticsEnabled)
{
ScriptFileMarker[] emptyAnalysisDiagnostics = new ScriptFileMarker[0];
- foreach (var scriptFile in WorkspaceService.Instance.Workspace.GetOpenedFiles())
+ foreach (var scriptFile in CurrentWorkspace.GetOpenedFiles())
{
- await DiagnosticsHelper.PublishScriptDiagnostics(scriptFile, emptyAnalysisDiagnostics, eventContext);
+ await DiagnosticsHelper.ClearScriptDiagnostics(scriptFile.ClientFilePath, eventContext);
}
}
// otherwise rerun diagnostic analysis on all opened SQL files
@@ -700,13 +718,158 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
}
- AutoCompleteHelper.PrepopulateCommonMetadata(info, scriptInfo, this.BindingQueue);
+ PrepopulateCommonMetadata(info, scriptInfo, this.BindingQueue);
// Send a notification to signal that autocomplete is ready
ServiceHost.Instance.SendEvent(IntelliSenseReadyNotification.Type, new IntelliSenseReadyParams() {OwnerUri = info.OwnerUri});
});
}
+
+ ///
+ /// Preinitialize the parser and binder with common metadata.
+ /// This should front load the long binding wait to the time the
+ /// connection is established. Once this is completed other binding
+ /// requests should be faster.
+ ///
+ ///
+ ///
+ internal void PrepopulateCommonMetadata(
+ ConnectionInfo info,
+ ScriptParseInfo scriptInfo,
+ ConnectedBindingQueue bindingQueue)
+ {
+ if (scriptInfo.IsConnected)
+ {
+ var scriptFile = CurrentWorkspace.GetFile(info.OwnerUri);
+ if (scriptFile == null)
+ {
+ return;
+ }
+
+ ParseAndBind(scriptFile, info);
+
+ if (Monitor.TryEnter(scriptInfo.BuildingMetadataLock, LanguageService.OnConnectionWaitTimeout))
+ {
+ try
+ {
+ QueueItem queueItem = bindingQueue.QueueBindingOperation(
+ key: scriptInfo.ConnectionKey,
+ bindingTimeout: PrepopulateBindTimeout,
+ waitForLockTimeout: PrepopulateBindTimeout,
+ bindOperation: (bindingContext, cancelToken) =>
+ {
+ // parse a simple statement that returns common metadata
+ ParseResult parseResult = Parser.Parse(
+ "select ",
+ bindingContext.ParseOptions);
+
+ List parseResults = new List();
+ parseResults.Add(parseResult);
+ bindingContext.Binder.Bind(
+ parseResults,
+ info.ConnectionDetails.DatabaseName,
+ BindMode.Batch);
+
+ // get the completion list from SQL Parser
+ var suggestions = Resolver.FindCompletions(
+ parseResult, 1, 8,
+ bindingContext.MetadataDisplayInfoProvider);
+
+ // this forces lazy evaluation of the suggestion metadata
+ AutoCompleteHelper.ConvertDeclarationsToCompletionItems(suggestions, 1, 8, 8);
+
+ parseResult = Parser.Parse(
+ "exec ",
+ bindingContext.ParseOptions);
+
+ parseResults = new List();
+ parseResults.Add(parseResult);
+ bindingContext.Binder.Bind(
+ parseResults,
+ info.ConnectionDetails.DatabaseName,
+ BindMode.Batch);
+
+ // get the completion list from SQL Parser
+ suggestions = Resolver.FindCompletions(
+ parseResult, 1, 6,
+ bindingContext.MetadataDisplayInfoProvider);
+
+ // this forces lazy evaluation of the suggestion metadata
+ AutoCompleteHelper.ConvertDeclarationsToCompletionItems(suggestions, 1, 6, 6);
+ return null;
+ });
+
+ queueItem.ItemProcessed.WaitOne();
+ }
+ catch
+ {
+ }
+ finally
+ {
+ Monitor.Exit(scriptInfo.BuildingMetadataLock);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Handles language flavor changes by disabling intellisense on a file if it does not match the specific
+ /// "MSSQL" language flavor returned by our service
+ ///
+ ///
+ public async Task HandleDidChangeLanguageFlavorNotification(
+ LanguageFlavorChangeParams changeParams,
+ EventContext eventContext)
+ {
+ Validate.IsNotNull(nameof(changeParams), changeParams);
+ Validate.IsNotNull(nameof(changeParams), changeParams.Uri);
+ bool shouldBlock = false;
+ if (SQL_LANG.Equals(changeParams.Language, StringComparison.OrdinalIgnoreCase)) {
+ shouldBlock = !ServiceHost.ProviderName.Equals(changeParams.Flavor, StringComparison.OrdinalIgnoreCase);
+ }
+
+ if (shouldBlock) {
+ this.nonMssqlUriMap.AddOrUpdate(changeParams.Uri, true, (k, oldValue) => true);
+ if (CurrentWorkspace.ContainsFile(changeParams.Uri))
+ {
+ await DiagnosticsHelper.ClearScriptDiagnostics(changeParams.Uri, eventContext);
+ }
+ }
+ else
+ {
+ bool value;
+ this.nonMssqlUriMap.TryRemove(changeParams.Uri, out value);
+ }
+ }
+
+ private bool ShouldSkipNonMssqlFile(TextDocumentPosition textDocPosition)
+ {
+ return ShouldSkipNonMssqlFile(textDocPosition.TextDocument.Uri);
+ }
+
+ private bool ShouldSkipNonMssqlFile(ScriptFile scriptFile)
+ {
+ return ShouldSkipNonMssqlFile(scriptFile.ClientFilePath);
+ }
+
+ private bool ShouldSkipNonMssqlFile(string uri)
+ {
+ bool isNonMssql = false;
+ nonMssqlUriMap.TryGetValue(uri, out isNonMssql);
+ return isNonMssql;
+ }
+
+ ///
+ /// Determines whether intellisense should be skipped for a document.
+ /// If IntelliSense is disabled or it's a non-MSSQL doc this will be skipped
+ ///
+ private bool ShouldSkipIntellisense(string uri)
+ {
+ return !CurrentWorkspaceSettings.IsSuggestionsEnabled
+ || ShouldSkipNonMssqlFile(uri);
+ }
+
///
/// Determines whether a reparse and bind is required to provide autocomplete
///
@@ -731,7 +894,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
internal CompletionItem ResolveCompletionItem(CompletionItem completionItem)
{
- var scriptParseInfo = LanguageService.Instance.currentCompletionParseInfo;
+ var scriptParseInfo = currentCompletionParseInfo;
if (scriptParseInfo != null && scriptParseInfo.CurrentSuggestions != null)
{
if (Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock))
@@ -1055,7 +1218,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
ConnectionInfo connInfo;
- LanguageService.ConnectionServiceInstance.TryFindConnection(
+ ConnectionServiceInstance.TryFindConnection(
scriptFile.ClientFilePath,
out connInfo);
@@ -1131,7 +1294,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
this.currentCompletionParseInfo = null;
CompletionItem[] resultCompletionItems = null;
CompletionService completionService = new CompletionService(BindingQueue);
- bool useLowerCaseSuggestions = this.CurrentSettings.SqlTools.IntelliSense.LowerCaseSuggestions.Value;
+ bool useLowerCaseSuggestions = this.CurrentWorkspaceSettings.SqlTools.IntelliSense.LowerCaseSuggestions.Value;
// get the current script parse info object
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri);
@@ -1179,7 +1342,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
internal ScriptFileMarker[] GetSemanticMarkers(ScriptFile scriptFile)
{
ConnectionInfo connInfo;
- ConnectionService.Instance.TryFindConnection(
+ ConnectionServiceInstance.TryFindConnection(
scriptFile.ClientFilePath,
out connInfo);
@@ -1219,7 +1382,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
private Task RunScriptDiagnostics(ScriptFile[] filesToAnalyze, EventContext eventContext)
{
- if (!CurrentSettings.IsDiagnositicsEnabled)
+ if (!CurrentWorkspaceSettings.IsDiagnosticsEnabled)
{
// If the user has disabled script analysis, skip it entirely
return Task.FromResult(true);
@@ -1228,15 +1391,15 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// If there's an existing task, attempt to cancel it
try
{
- if (ExistingRequestCancellation != null)
+ if (existingRequestCancellation != null)
{
// Try to cancel the request
- ExistingRequestCancellation.Cancel();
+ existingRequestCancellation.Cancel();
// If cancellation didn't throw an exception,
// clean up the existing token
- ExistingRequestCancellation.Dispose();
- ExistingRequestCancellation = null;
+ existingRequestCancellation.Dispose();
+ existingRequestCancellation = null;
}
}
catch (Exception e)
@@ -1251,14 +1414,14 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// 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();
+ existingRequestCancellation = new CancellationTokenSource();
Task.Factory.StartNew(
() =>
DelayThenInvokeDiagnostics(
LanguageService.DiagnosticParseDelay,
filesToAnalyze,
eventContext,
- ExistingRequestCancellation.Token),
+ existingRequestCancellation.Token),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);
@@ -1305,6 +1468,12 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{
continue;
}
+ else if (ShouldSkipNonMssqlFile(scriptFile.ClientFilePath))
+ {
+ // Clear out any existing markers in case file type was changed
+ await DiagnosticsHelper.ClearScriptDiagnostics(scriptFile.ClientFilePath, eventContext);
+ continue;
+ }
Logger.Write(LogLevel.Verbose, "Analyzing script file: " + scriptFile.FilePath);
ScriptFileMarker[] semanticMarkers = GetSemanticMarkers(scriptFile);
@@ -1405,4 +1574,4 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs
index ee92cfa3..777791a0 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs
@@ -47,7 +47,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
}
else
{
- ErrorMessage = string.Format(CultureInfo.InvariantCulture, SR.DatabaseNotAccessible, context.Database.Name);
+ if (string.IsNullOrEmpty(ErrorMessage))
+ {
+ // Write error message if it wasn't already set during IsAccessible check
+ ErrorMessage = string.Format(CultureInfo.InvariantCulture, SR.DatabaseNotAccessible, context.Database.Name);
+ }
}
}
@@ -63,11 +67,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
}
catch (Exception ex)
{
- return true;
string error = string.Format(CultureInfo.InvariantCulture, "Failed to get IsAccessible. error:{0} inner:{1} stacktrace:{2}",
ex.Message, ex.InnerException != null ? ex.InnerException.Message : "", ex.StackTrace);
Logger.Write(LogLevel.Error, error);
ErrorMessage = ex.Message;
+ return false;
}
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs b/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs
index 09356f69..b865179f 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs
@@ -8,15 +8,15 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
-using Microsoft.SqlTools.Extensibility;
+using Microsoft.SqlTools.Extensibility;
using Microsoft.SqlTools.Hosting;
using Microsoft.SqlTools.Hosting.Contracts;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.Hosting.Protocol.Channel;
using Microsoft.SqlTools.Utility;
-using Microsoft.SqlTools.ServiceLayer.Connection;
-using Microsoft.SqlTools.ServiceLayer.Admin;
-
+using Microsoft.SqlTools.ServiceLayer.Connection;
+using Microsoft.SqlTools.ServiceLayer.Admin;
+
namespace Microsoft.SqlTools.ServiceLayer.Hosting
{
///
@@ -26,7 +26,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
///
public sealed class ServiceHost : ServiceHostBase
{
- private const string ProviderName = "MSSQL";
+ public const string ProviderName = "MSSQL";
private const string ProviderDescription = "Microsoft SQL Server";
private const string ProviderProtocolVersion = "1.0";
@@ -62,17 +62,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
// Initialize the shutdown activities
shutdownCallbacks = new List();
initializeCallbacks = new List();
- }
-
+ }
+
public IMultiServiceProvider ServiceProvider
{
- get
- {
- return serviceProvider;
+ get
+ {
+ return serviceProvider;
}
- internal set
- {
- serviceProvider = value;
+ internal set
+ {
+ serviceProvider = value;
}
}
@@ -192,8 +192,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
});
}
- ///
- /// Handles a request for the capabilities request
+ ///
+ /// Handles a request for the capabilities request
///
internal async Task HandleCapabilitiesRequest(
CapabilitiesRequest initializeParams,
diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlContext/SqlToolsSettings.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlContext/SqlToolsSettings.cs
index d91a3453..d2ddf9b3 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/SqlContext/SqlToolsSettings.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/SqlContext/SqlToolsSettings.cs
@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlContext
///
/// Gets a flag determining if diagnostics are enabled
///
- public bool IsDiagnositicsEnabled
+ public bool IsDiagnosticsEnabled
{
get
{
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/LanguageServer/LanguageServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/LanguageServer/LanguageServiceTests.cs
index 1458c202..d6ef38e3 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/LanguageServer/LanguageServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/LanguageServer/LanguageServiceTests.cs
@@ -52,8 +52,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
}
Assert.True(LanguageService.Instance.Context != null);
- Assert.True(LanguageService.ConnectionServiceInstance != null);
- Assert.True(LanguageService.Instance.CurrentSettings != null);
+ Assert.True(LanguageService.Instance.ConnectionServiceInstance != null);
+ Assert.True(LanguageService.Instance.CurrentWorkspaceSettings != null);
Assert.True(LanguageService.Instance.CurrentWorkspace != null);
}
@@ -68,7 +68,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
ScriptParseInfo scriptInfo = new ScriptParseInfo { IsConnected = true };
- AutoCompleteHelper.PrepopulateCommonMetadata(connInfo, scriptInfo, null);
+ LanguageService.Instance.PrepopulateCommonMetadata(connInfo, scriptInfo, null);
}
// This test currently requires a live database connection to initialize
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/AutocompleteTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/AutocompleteTests.cs
index c6125823..d8bacbbd 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/AutocompleteTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/AutocompleteTests.cs
@@ -19,126 +19,71 @@ using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
using GlobalCommon = Microsoft.SqlTools.ServiceLayer.Test.Common;
using Moq;
using Xunit;
+using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
{
///
/// Tests for the language service autocomplete component
///
- public class AutocompleteTests
+ public class AutocompleteTests : LanguageServiceTestBase
{
- private const int TaskTimeout = 60000;
-
- private readonly string testScriptUri = TestObjects.ScriptUri;
-
- private readonly string testConnectionKey = "testdbcontextkey";
-
- private Mock bindingQueue;
-
- private Mock> workspaceService;
-
- private Mock> requestContext;
-
- private Mock scriptFile;
-
- private Mock binder;
-
- private ScriptParseInfo scriptParseInfo;
-
- private TextDocumentPosition textDocument;
-
- private void InitializeTestObjects()
- {
- // initial cursor position in the script file
- textDocument = new TextDocumentPosition
- {
- TextDocument = new TextDocumentIdentifier {Uri = this.testScriptUri},
- Position = new Position
- {
- Line = 0,
- Character = 0
- }
- };
-
- // default settings are stored in the workspace service
- WorkspaceService.Instance.CurrentSettings = new SqlToolsSettings();
-
- // set up file for returning the query
- scriptFile = new Mock();
- scriptFile.SetupGet(file => file.Contents).Returns(GlobalCommon.Constants.StandardQuery);
- scriptFile.SetupGet(file => file.ClientFilePath).Returns(this.testScriptUri);
-
- // set up workspace mock
- workspaceService = new Mock>();
- workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny()))
- .Returns(scriptFile.Object);
-
- // setup binding queue mock
- bindingQueue = new Mock();
- bindingQueue.Setup(q => q.AddConnectionContext(It.IsAny(), It.IsAny()))
- .Returns(this.testConnectionKey);
-
- // inject mock instances into the Language Service
- LanguageService.WorkspaceServiceInstance = workspaceService.Object;
- LanguageService.ConnectionServiceInstance = TestObjects.GetTestConnectionService();
- ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
- LanguageService.ConnectionServiceInstance.OwnerToConnectionMap.Add(this.testScriptUri, connectionInfo);
- LanguageService.Instance.BindingQueue = bindingQueue.Object;
-
- // setup the mock for SendResult
- requestContext = new Mock>();
- requestContext.Setup(rc => rc.SendResult(It.IsAny()))
- .Returns(Task.FromResult(0));
-
- // setup the IBinder mock
- binder = new Mock();
- binder.Setup(b => b.Bind(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()));
-
- scriptParseInfo = new ScriptParseInfo();
- LanguageService.Instance.AddOrUpdateScriptParseInfo(this.testScriptUri, scriptParseInfo);
- scriptParseInfo.IsConnected = true;
- scriptParseInfo.ConnectionKey = LanguageService.Instance.BindingQueue.AddConnectionContext(connectionInfo);
-
- // setup the binding context object
- ConnectedBindingContext bindingContext = new ConnectedBindingContext();
- bindingContext.Binder = binder.Object;
- bindingContext.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider();
- LanguageService.Instance.BindingQueue.BindingContextMap.Add(scriptParseInfo.ConnectionKey, bindingContext);
- }
[Fact]
public void HandleCompletionRequestDisabled()
{
InitializeTestObjects();
- WorkspaceService.Instance.CurrentSettings.SqlTools.IntelliSense.EnableIntellisense = false;
- Assert.NotNull(LanguageService.HandleCompletionRequest(null, null));
+ langService.CurrentWorkspaceSettings.SqlTools.IntelliSense.EnableIntellisense = false;
+ Assert.NotNull(langService.HandleCompletionRequest(null, null));
}
[Fact]
public void HandleCompletionResolveRequestDisabled()
{
InitializeTestObjects();
- WorkspaceService.Instance.CurrentSettings.SqlTools.IntelliSense.EnableIntellisense = false;
- Assert.NotNull(LanguageService.HandleCompletionResolveRequest(null, null));
+ langService.CurrentWorkspaceSettings.SqlTools.IntelliSense.EnableIntellisense = false;
+ Assert.NotNull(langService.HandleCompletionResolveRequest(null, null));
}
[Fact]
public void HandleSignatureHelpRequestDisabled()
{
InitializeTestObjects();
- WorkspaceService.Instance.CurrentSettings.SqlTools.IntelliSense.EnableIntellisense = false;
- Assert.NotNull(LanguageService.HandleSignatureHelpRequest(null, null));
+ langService.CurrentWorkspaceSettings.SqlTools.IntelliSense.EnableIntellisense = false;
+ Assert.NotNull(langService.HandleSignatureHelpRequest(null, null));
+ }
+
+ [Fact]
+ public void HandleSignatureHelpRequestNonMssqlFile()
+ {
+ InitializeTestObjects();
+
+ // setup the mock for SendResult
+ var signatureRequestContext = new Mock>();
+ signatureRequestContext.Setup(rc => rc.SendResult(It.IsAny()))
+ .Returns(Task.FromResult(0));
+ signatureRequestContext.Setup(rc => rc.SendError(It.IsAny(), It.IsAny())).Returns(Task.FromResult(0));
+
+
+ langService.CurrentWorkspaceSettings.SqlTools.IntelliSense.EnableIntellisense = true;
+ langService.HandleDidChangeLanguageFlavorNotification(new LanguageFlavorChangeParams {
+ Uri = textDocument.TextDocument.Uri,
+ Language = LanguageService.SQL_LANG.ToLower(),
+ Flavor = "NotMSSQL"
+ }, null);
+ Assert.NotNull(langService.HandleSignatureHelpRequest(textDocument, signatureRequestContext.Object));
+
+ // verify that no events were sent
+ signatureRequestContext.Verify(m => m.SendResult(It.IsAny()), Times.Never());
+ signatureRequestContext.Verify(m => m.SendError(It.IsAny(), It.IsAny()), Times.Never());
}
[Fact]
public void AddOrUpdateScriptParseInfoNullUri()
{
InitializeTestObjects();
- LanguageService.Instance.AddOrUpdateScriptParseInfo("abracadabra", scriptParseInfo);
- Assert.True(LanguageService.Instance.ScriptParseInfoMap.ContainsKey("abracadabra"));
+ langService.AddOrUpdateScriptParseInfo("abracadabra", scriptParseInfo);
+ Assert.True(langService.ScriptParseInfoMap.ContainsKey("abracadabra"));
}
[Fact]
@@ -146,21 +91,21 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
{
InitializeTestObjects();
textDocument.TextDocument.Uri = "invaliduri";
- Assert.Null(LanguageService.Instance.GetDefinition(textDocument, null, null));
+ Assert.Null(langService.GetDefinition(textDocument, null, null));
}
[Fact]
public void RemoveScriptParseInfoNullUri()
{
InitializeTestObjects();
- Assert.False(LanguageService.Instance.RemoveScriptParseInfo("abc123"));
+ Assert.False(langService.RemoveScriptParseInfo("abc123"));
}
[Fact]
public void IsPreviewWindowNullScriptFileTest()
{
InitializeTestObjects();
- Assert.False(LanguageService.Instance.IsPreviewWindow(null));
+ Assert.False(langService.IsPreviewWindow(null));
}
[Fact]
@@ -168,7 +113,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
{
InitializeTestObjects();
textDocument.TextDocument.Uri = "somethinggoeshere";
- Assert.True(LanguageService.Instance.GetCompletionItems(textDocument, scriptFile.Object, null).Length > 0);
+ Assert.True(langService.GetCompletionItems(textDocument, scriptFile.Object, null).Length > 0);
}
[Fact]
@@ -215,7 +160,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
InitializeTestObjects();
// request the completion list
- Task handleCompletion = LanguageService.HandleCompletionRequest(textDocument, requestContext.Object);
+ Task handleCompletion = langService.HandleCompletionRequest(textDocument, requestContext.Object);
handleCompletion.Wait(TaskTimeout);
// verify that send result was called with a completion array
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/LanguageServiceTestBase.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/LanguageServiceTestBase.cs
new file mode 100644
index 00000000..4642c727
--- /dev/null
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/LanguageServiceTestBase.cs
@@ -0,0 +1,119 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.SqlServer.Management.SqlParser.Binder;
+using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
+using Microsoft.SqlServer.Management.SqlParser.Parser;
+using Microsoft.SqlTools.Hosting.Protocol;
+using Microsoft.SqlTools.Hosting.Protocol.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Connection;
+using Microsoft.SqlTools.ServiceLayer.LanguageServices;
+using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
+using Microsoft.SqlTools.ServiceLayer.SqlContext;
+using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
+using Microsoft.SqlTools.ServiceLayer.Workspace;
+using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
+using GlobalCommon = Microsoft.SqlTools.ServiceLayer.Test.Common;
+using Moq;
+using Xunit;
+
+namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
+{
+ ///
+ /// Tests for the language service autocomplete component
+ ///
+ public abstract class LanguageServiceTestBase
+ {
+ protected const int TaskTimeout = 60000;
+
+ protected readonly string testScriptUri = TestObjects.ScriptUri;
+
+ protected readonly string testConnectionKey = "testdbcontextkey";
+
+ protected LanguageService langService;
+
+ protected Mock bindingQueue;
+
+ protected Mock> workspaceService;
+
+ protected Mock> requestContext;
+
+ protected Mock scriptFile;
+
+ protected Mock binder;
+
+ internal ScriptParseInfo scriptParseInfo;
+
+ protected TextDocumentPosition textDocument;
+
+ protected void InitializeTestObjects()
+ {
+ // initial cursor position in the script file
+ textDocument = new TextDocumentPosition
+ {
+ TextDocument = new TextDocumentIdentifier { Uri = this.testScriptUri },
+ Position = new Position
+ {
+ Line = 0,
+ Character = 23
+ }
+ };
+
+ // default settings are stored in the workspace service
+ WorkspaceService.Instance.CurrentSettings = new SqlToolsSettings();
+
+ // set up file for returning the query
+ scriptFile = new Mock();
+ scriptFile.SetupGet(file => file.Contents).Returns(GlobalCommon.Constants.StandardQuery);
+ scriptFile.SetupGet(file => file.ClientFilePath).Returns(this.testScriptUri);
+
+ // set up workspace mock
+ workspaceService = new Mock>();
+ workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny()))
+ .Returns(scriptFile.Object);
+
+ // setup binding queue mock
+ bindingQueue = new Mock();
+ bindingQueue.Setup(q => q.AddConnectionContext(It.IsAny(), It.IsAny()))
+ .Returns(this.testConnectionKey);
+
+ langService = new LanguageService();
+ // inject mock instances into the Language Service
+ langService.WorkspaceServiceInstance = workspaceService.Object;
+ langService.ConnectionServiceInstance = TestObjects.GetTestConnectionService();
+ ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
+ langService.ConnectionServiceInstance.OwnerToConnectionMap.Add(this.testScriptUri, connectionInfo);
+ langService.BindingQueue = bindingQueue.Object;
+
+ // setup the mock for SendResult
+ requestContext = new Mock>();
+ requestContext.Setup(rc => rc.SendResult(It.IsAny()))
+ .Returns(Task.FromResult(0));
+ requestContext.Setup(rc => rc.SendError(It.IsAny(), It.IsAny())).Returns(Task.FromResult(0));
+ requestContext.Setup(r => r.SendEvent(It.IsAny>(), It.IsAny())).Returns(Task.FromResult(0));
+ requestContext.Setup(r => r.SendEvent(It.IsAny>(), It.IsAny())).Returns(Task.FromResult(0));
+
+ // setup the IBinder mock
+ binder = new Mock();
+ binder.Setup(b => b.Bind(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny()));
+
+ scriptParseInfo = new ScriptParseInfo();
+ langService.AddOrUpdateScriptParseInfo(this.testScriptUri, scriptParseInfo);
+ scriptParseInfo.IsConnected = true;
+ scriptParseInfo.ConnectionKey = langService.BindingQueue.AddConnectionContext(connectionInfo);
+
+ // setup the binding context object
+ ConnectedBindingContext bindingContext = new ConnectedBindingContext();
+ bindingContext.Binder = binder.Object;
+ bindingContext.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider();
+ langService.BindingQueue.BindingContextMap.Add(scriptParseInfo.ConnectionKey, bindingContext);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/LanguageServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/LanguageServiceTests.cs
index 57ad0fc4..8e84a9ec 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/LanguageServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/LanguageServiceTests.cs
@@ -150,14 +150,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
Assert.Equal(AutoCompleteHelper.EmptyCompletionList.Length, 0);
}
- [Fact]
- public void SetWorkspaceServiceInstanceTest()
- {
- AutoCompleteHelper.WorkspaceServiceInstance = null;
- // workspace will be recreated if it's set to null
- Assert.NotNull(AutoCompleteHelper.WorkspaceServiceInstance);
- }
-
internal class TestScriptDocumentInfo : ScriptDocumentInfo
{
public TestScriptDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile, ScriptParseInfo scriptParseInfo,
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/PeekDefinitionTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/PeekDefinitionTests.cs
index b10f5877..4a18f4df 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/PeekDefinitionTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/LanguageServer/PeekDefinitionTests.cs
@@ -33,90 +33,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
///
/// Tests for the language service peek definition/ go to definition feature
///
- public class PeekDefinitionTests
+ public class PeekDefinitionTests : LanguageServiceTestBase
{
- private const int TaskTimeout = 30000;
-
- private readonly string testScriptUri = TestObjects.ScriptUri;
-
- private readonly string testConnectionKey = "testdbcontextkey";
-
- private Mock bindingQueue;
-
- private Mock> workspaceService;
-
- private Mock> requestContext;
-
- private Mock binder;
-
- private TextDocumentPosition textDocument;
-
- private void InitializeTestObjects()
- {
- // initial cursor position in the script file
- textDocument = new TextDocumentPosition
- {
- TextDocument = new TextDocumentIdentifier {Uri = this.testScriptUri},
- Position = new Position
- {
- Line = 0,
- Character = 23
- }
- };
-
- // default settings are stored in the workspace service
- WorkspaceService.Instance.CurrentSettings = new SqlToolsSettings();
-
- // set up file for returning the query
- var fileMock = new Mock();
- fileMock.SetupGet(file => file.Contents).Returns(GlobalCommon.Constants.StandardQuery);
- fileMock.SetupGet(file => file.ClientFilePath).Returns(this.testScriptUri);
-
- // set up workspace mock
- workspaceService = new Mock>();
- workspaceService.Setup(service => service.Workspace.GetFile(It.IsAny()))
- .Returns(fileMock.Object);
-
- // setup binding queue mock
- bindingQueue = new Mock();
- bindingQueue.Setup(q => q.AddConnectionContext(It.IsAny(), It.IsAny()))
- .Returns(this.testConnectionKey);
-
- // inject mock instances into the Language Service
- LanguageService.WorkspaceServiceInstance = workspaceService.Object;
- LanguageService.ConnectionServiceInstance = TestObjects.GetTestConnectionService();
- ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
- LanguageService.ConnectionServiceInstance.OwnerToConnectionMap.Add(this.testScriptUri, connectionInfo);
- LanguageService.Instance.BindingQueue = bindingQueue.Object;
-
- // setup the mock for SendResult
- requestContext = new Mock>();
- requestContext.Setup(rc => rc.SendResult(It.IsAny()))
- .Returns(Task.FromResult(0));
- requestContext.Setup(rc => rc.SendError(It.IsAny(), It.IsAny())).Returns(Task.FromResult(0));
- requestContext.Setup(r => r.SendEvent(It.IsAny>(), It.IsAny())).Returns(Task.FromResult(0));
- requestContext.Setup(r => r.SendEvent(It.IsAny>(), It.IsAny())).Returns(Task.FromResult(0));
-
- // setup the IBinder mock
- binder = new Mock();
- binder.Setup(b => b.Bind(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()));
-
- var testScriptParseInfo = new ScriptParseInfo();
- LanguageService.Instance.AddOrUpdateScriptParseInfo(this.testScriptUri, testScriptParseInfo);
- testScriptParseInfo.IsConnected = false;
- testScriptParseInfo.ConnectionKey = LanguageService.Instance.BindingQueue.AddConnectionContext(connectionInfo);
-
- // setup the binding context object
- ConnectedBindingContext bindingContext = new ConnectedBindingContext();
- bindingContext.Binder = binder.Object;
- bindingContext.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider();
- LanguageService.Instance.BindingQueue.BindingContextMap.Add(testScriptParseInfo.ConnectionKey, bindingContext);
- }
-
-
///
/// Tests the definition event handler. When called with no active connection, an error is sent
///
@@ -124,8 +42,9 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
public async Task DefinitionsHandlerWithNoConnectionTest()
{
InitializeTestObjects();
+ scriptParseInfo.IsConnected = false;
// request definition
- var definitionTask = await Task.WhenAny(LanguageService.HandleDefinitionRequest(textDocument, requestContext.Object), Task.Delay(TaskTimeout));
+ var definitionTask = await Task.WhenAny(langService.HandleDefinitionRequest(textDocument, requestContext.Object), Task.Delay(TaskTimeout));
await definitionTask;
// verify that send result was not called and send error was called
requestContext.Verify(m => m.SendResult(It.IsAny()), Times.Never());
@@ -199,9 +118,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
public void DeletePeekDefinitionScriptsTest()
{
Scripter peekDefinition = new Scripter(null, null);
- var languageService = LanguageService.Instance;
Assert.True(Directory.Exists(FileUtilities.PeekDefinitionTempFolder));
- languageService.DeletePeekDefinitionScripts();
+ LanguageService.Instance.DeletePeekDefinitionScripts();
Assert.False(Directory.Exists(FileUtilities.PeekDefinitionTempFolder));
}
@@ -211,12 +129,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
[Fact]
public void DeletePeekDefinitionScriptsWhenFolderDoesNotExistTest()
{
- var languageService = LanguageService.Instance;
Scripter peekDefinition = new Scripter(null, null);
FileUtilities.SafeDirectoryDelete(FileUtilities.PeekDefinitionTempFolder, true);
Assert.False(Directory.Exists(FileUtilities.PeekDefinitionTempFolder));
// Expected not to throw any exception
- languageService.DeletePeekDefinitionScripts();
+ LanguageService.Instance.DeletePeekDefinitionScripts();
}
///
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/SqlContext/SettingsTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/SqlContext/SettingsTests.cs
index db2093ba..f76ac8e5 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/SqlContext/SettingsTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/SqlContext/SettingsTests.cs
@@ -20,7 +20,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.SqlContext
public void ValidateLanguageServiceDefaults()
{
var sqlToolsSettings = new SqlToolsSettings();
- Assert.True(sqlToolsSettings.IsDiagnositicsEnabled);
+ Assert.True(sqlToolsSettings.IsDiagnosticsEnabled);
Assert.True(sqlToolsSettings.IsSuggestionsEnabled);
Assert.True(sqlToolsSettings.SqlTools.IntelliSense.EnableIntellisense);
Assert.True(sqlToolsSettings.SqlTools.IntelliSense.EnableErrorChecking);
@@ -30,7 +30,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.SqlContext
}
///
- /// Validate that the IsDiagnositicsEnabled flag behavior
+ /// Validate that the IsDiagnosticsEnabled flag behavior
///
[Fact]
public void ValidateIsDiagnosticsEnabled()
@@ -40,16 +40,16 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.SqlContext
// diagnostics is enabled if IntelliSense and Diagnostics flags are set
sqlToolsSettings.SqlTools.IntelliSense.EnableIntellisense = true;
sqlToolsSettings.SqlTools.IntelliSense.EnableErrorChecking = true;
- Assert.True(sqlToolsSettings.IsDiagnositicsEnabled);
+ Assert.True(sqlToolsSettings.IsDiagnosticsEnabled);
// diagnostics is disabled if either IntelliSense and Diagnostics flags is not set
sqlToolsSettings.SqlTools.IntelliSense.EnableIntellisense = false;
sqlToolsSettings.SqlTools.IntelliSense.EnableErrorChecking = true;
- Assert.False(sqlToolsSettings.IsDiagnositicsEnabled);
+ Assert.False(sqlToolsSettings.IsDiagnosticsEnabled);
sqlToolsSettings.SqlTools.IntelliSense.EnableIntellisense = true;
sqlToolsSettings.SqlTools.IntelliSense.EnableErrorChecking = false;
- Assert.False(sqlToolsSettings.IsDiagnositicsEnabled);
+ Assert.False(sqlToolsSettings.IsDiagnosticsEnabled);
}
///