diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/BindingQueue.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/BindingQueue.cs
index b7808509..bf78583e 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/BindingQueue.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/BindingQueue.cs
@@ -123,6 +123,28 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
}
+ ///
+ /// Remove the binding queue entry
+ ///
+ protected void RemoveBindingContext(string key)
+ {
+ lock (this.bindingContextLock)
+ {
+ if (this.BindingContextMap.ContainsKey(key))
+ {
+ // disconnect existing connection
+ var bindingContext = this.BindingContextMap[key];
+ if (bindingContext.ServerConnection != null && bindingContext.ServerConnection.IsOpen)
+ {
+ bindingContext.ServerConnection.Disconnect();
+ }
+
+ // remove key from the map
+ this.BindingContextMap.Remove(key);
+ }
+ }
+ }
+
private bool HasPendingQueueItems
{
get
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs
index bd767a7a..a74810d5 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ConnectedBindingQueue.cs
@@ -51,8 +51,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
///
/// Use a ConnectionInfo item to create a connected binding context
///
- ///
- public virtual string AddConnectionContext(ConnectionInfo connInfo)
+ /// Connection info used to create binding context
+ /// Overwrite existing context
+ public virtual string AddConnectionContext(ConnectionInfo connInfo, bool overwrite = false)
{
if (connInfo == null)
{
@@ -63,8 +64,15 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
string connectionKey = GetConnectionContextKey(connInfo);
if (BindingContextExists(connectionKey))
{
- // no need to populate the context again since the context already exists
- return connectionKey;
+ if (overwrite)
+ {
+ RemoveBindingContext(connectionKey);
+ }
+ else
+ {
+ // no need to populate the context again since the context already exists
+ return connectionKey;
+ }
}
IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey);
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/Contracts/RebuildIntelliSenseNotification.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/Contracts/RebuildIntelliSenseNotification.cs
new file mode 100644
index 00000000..f5c52591
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/Contracts/RebuildIntelliSenseNotification.cs
@@ -0,0 +1,30 @@
+//
+// 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.ServiceLayer.Hosting.Protocol.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
+{
+ ///
+ /// Parameters to be sent back with a rebuild IntelliSense event
+ ///
+ public class RebuildIntelliSenseParams
+ {
+ ///
+ /// URI identifying the file that should have its IntelliSense cache rebuilt
+ ///
+ public string OwnerUri { get; set; }
+ }
+
+ ///
+ /// RebuildIntelliSenseNotification notification mapping entry
+ ///
+ public class RebuildIntelliSenseNotification
+ {
+ public static readonly
+ EventType Type =
+ EventType.Create("textDocument/rebuildIntelliSense");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
index 46a59afb..2229f893 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs
@@ -212,6 +212,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
serviceHost.SetRequestHandler(HoverRequest.Type, HandleHoverRequest);
serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
serviceHost.SetRequestHandler(DefinitionRequest.Type, HandleDefinitionRequest);
+ serviceHost.SetEventHandler(RebuildIntelliSenseNotification.Type, HandleRebuildIntelliSenseNotification);
// Register a no-op shutdown task for validation of the shutdown logic
serviceHost.RegisterShutdownTask(async (shutdownParams, shutdownRequestContext) =>
@@ -442,6 +443,62 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
await Task.FromResult(true);
}
+ ///
+ /// Handle the rebuild IntelliSense cache notification
+ ///
+ public async Task HandleRebuildIntelliSenseNotification(
+ RebuildIntelliSenseParams configChangeParams,
+ EventContext eventContext)
+ {
+ Logger.Write(LogLevel.Verbose, "HandleRebuildIntelliSenseNotification");
+
+ // Skip closing this file if the file doesn't exist
+ var scriptFile = this.CurrentWorkspace.GetFile(configChangeParams.OwnerUri);
+ if (scriptFile == null)
+ {
+ return;
+ }
+
+ ConnectionInfo connInfo;
+ LanguageService.ConnectionServiceInstance.TryFindConnection(
+ scriptFile.ClientFilePath,
+ out connInfo);
+
+ await Task.Run(() =>
+ {
+ ScriptParseInfo scriptInfo = GetScriptParseInfo(connInfo.OwnerUri, createIfNotExists: false);
+ if (scriptInfo != null && scriptInfo.IsConnected &&
+ Monitor.TryEnter(scriptInfo.BuildingMetadataLock, LanguageService.OnConnectionWaitTimeout))
+ {
+ try
+ {
+ this.BindingQueue.AddConnectionContext(connInfo, overwrite: true);
+ }
+ catch (Exception ex)
+ {
+ Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString());
+ }
+ finally
+ {
+ // Set Metadata Build event to Signal state.
+ Monitor.Exit(scriptInfo.BuildingMetadataLock);
+ }
+ }
+
+ // if not in the preview window and diagnostics are enabled then run diagnostics
+ if (!IsPreviewWindow(scriptFile)
+ && WorkspaceService.Instance.CurrentSettings.IsDiagnositicsEnabled)
+ {
+ RunScriptDiagnostics(
+ new ScriptFile[] { scriptFile },
+ eventContext);
+ }
+
+ // Send a notification to signal that autocomplete is ready
+ ServiceHost.Instance.SendEvent(IntelliSenseReadyNotification.Type, new IntelliSenseReadyParams() {OwnerUri = connInfo.OwnerUri});
+ });
+ }
+
///
/// Handle the file configuration change notification
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Workspace/WorkspaceService.cs b/src/Microsoft.SqlTools.ServiceLayer/Workspace/WorkspaceService.cs
index dad14f90..5b5102e9 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Workspace/WorkspaceService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Workspace/WorkspaceService.cs
@@ -112,6 +112,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
/// List of callbacks to call when a text document is closed
///
private List TextDocCloseCallbacks { get; set; }
+
#endregion
@@ -189,7 +190,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
public void RegisterTextDocOpenCallback(TextDocOpenCallback task)
{
TextDocOpenCallbacks.Add(task);
- }
+ }
#endregion
@@ -287,7 +288,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
var configUpdateTasks = ConfigChangeCallbacks.Select(
t => t(configChangeParams.Settings, CurrentSettings, eventContext));
await Task.WhenAll(configUpdateTasks);
- }
+ }
#endregion
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/LanguageServer/LanguageServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/LanguageServer/LanguageServiceTests.cs
index 31747992..2b52b951 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/LanguageServer/LanguageServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/LanguageServer/LanguageServiceTests.cs
@@ -135,5 +135,27 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
SignatureHelp signatureHelp = service.GetSignatureHelp(textDocument, result.ScriptFile);
Assert.NotNull(signatureHelp);
}
+
+ ///
+ /// Test overwriting the binding queue context
+ ///
+ [Fact]
+ public void OverwriteBindingContext()
+ {
+ var result = TestObjects.InitLiveConnectionInfo();
+
+ // add a new connection context
+ var connectionKey = LanguageService.Instance.BindingQueue.AddConnectionContext(result.ConnectionInfo, overwrite: true);
+ Assert.True(LanguageService.Instance.BindingQueue.BindingContextMap.ContainsKey(connectionKey));
+
+ // cache the server connection
+ var orgServerConnection = LanguageService.Instance.BindingQueue.BindingContextMap[connectionKey].ServerConnection;
+ Assert.NotNull(orgServerConnection);
+
+ // add a new connection context
+ connectionKey = LanguageService.Instance.BindingQueue.AddConnectionContext(result.ConnectionInfo, overwrite: true);
+ Assert.True(LanguageService.Instance.BindingQueue.BindingContextMap.ContainsKey(connectionKey));
+ Assert.False(object.ReferenceEquals(LanguageService.Instance.BindingQueue.BindingContextMap[connectionKey].ServerConnection, orgServerConnection));
+ }
}
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/AutocompleteTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/AutocompleteTests.cs
index db5014cd..4fcc14fa 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/AutocompleteTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/AutocompleteTests.cs
@@ -75,7 +75,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
// setup binding queue mock
bindingQueue = new Mock();
- bindingQueue.Setup(q => q.AddConnectionContext(It.IsAny()))
+ bindingQueue.Setup(q => q.AddConnectionContext(It.IsAny(), It.IsAny()))
.Returns(this.testConnectionKey);
// inject mock instances into the Language Service
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/PeekDefinitionTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/PeekDefinitionTests.cs
index 0b7efbb7..675b3507 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/PeekDefinitionTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/PeekDefinitionTests.cs
@@ -82,7 +82,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
// setup binding queue mock
bindingQueue = new Mock();
- bindingQueue.Setup(q => q.AddConnectionContext(It.IsAny()))
+ bindingQueue.Setup(q => q.AddConnectionContext(It.IsAny(), It.IsAny()))
.Returns(this.testConnectionKey);
// inject mock instances into the Language Service