diff --git a/nuget.config b/nuget.config
index a839b559..933ad9ee 100644
--- a/nuget.config
+++ b/nuget.config
@@ -9,7 +9,6 @@
-
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs
index 290a8680..8f430e29 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs
@@ -38,17 +38,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
public ConnectionDetails ConnectionDetails { get; private set; }
- public DbConnection SqlConnection { get; private set; }
-
- public void OpenConnection()
- {
- // build the connection string from the input parameters
- string connectionString = ConnectionService.BuildConnectionString(ConnectionDetails);
-
- // create a sql connection instance
- SqlConnection = Factory.CreateSqlConnection(connectionString);
- SqlConnection.Open();
- }
+ public DbConnection SqlConnection { get; set; }
}
///
@@ -170,7 +160,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
var response = new ConnectResponse();
try
{
- connectionInfo.OpenConnection();
+ // build the connection string from the input parameters
+ string connectionString = ConnectionService.BuildConnectionString(connectionInfo.ConnectionDetails);
+
+ // create a sql connection instance
+ connectionInfo.SqlConnection = connectionInfo.Factory.CreateSqlConnection(connectionString);
+ connectionInfo.SqlConnection.Open();
}
catch(Exception ex)
{
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionMessages.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectMessages.cs
similarity index 63%
rename from src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionMessages.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectMessages.cs
index aa27da3e..543b18f5 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionMessages.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectMessages.cs
@@ -27,31 +27,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
}
///
- /// Parameters for the Disconnect Request.
+ /// Message format for the connection result response
///
- public class DisconnectParams
+ public class ConnectResponse
{
///
- /// A URI identifying the owner of the connection. This will most commonly be a file in the workspace
- /// or a virtual file representing an object in a database.
+ /// A GUID representing a unique connection ID
///
- public string OwnerUri { get; set; }
- }
+ public string ConnectionId { get; set; }
- ///
- /// Parameters for the ConnectionChanged Notification.
- ///
- public class ConnectionChangedParams
- {
///
- /// A URI identifying the owner of the connection. This will most commonly be a file in the workspace
- /// or a virtual file representing an object in a database.
+ /// Gets or sets any connection error messages
///
- public string OwnerUri { get; set; }
- ///
- /// Contains the high-level properties about the connection, for display to the user.
- ///
- public ConnectionSummary Connection { get; set; }
+ public string Messages { get; set; }
}
///
@@ -74,6 +62,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
///
public string UserName { get; set; }
}
+
///
/// Message format for the initial connection request
///
@@ -88,22 +77,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
// TODO Handle full set of properties
}
- ///
- /// Message format for the connection result response
- ///
- public class ConnectResponse
- {
- ///
- /// A GUID representing a unique connection ID
- ///
- public string ConnectionId { get; set; }
-
- ///
- /// Gets or sets any connection error messages
- ///
- public string Messages { get; set; }
- }
-
///
/// Connect request mapping entry
///
@@ -113,25 +86,4 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
RequestType Type =
RequestType.Create("connection/connect");
}
-
- ///
- /// Disconnect request mapping entry
- ///
- public class DisconnectRequest
- {
- public static readonly
- RequestType Type =
- RequestType.Create("connection/disconnect");
- }
-
- ///
- /// ConnectionChanged notification mapping entry
- ///
- public class ConnectionChangedNotification
- {
- public static readonly
- EventType Type =
- EventType.Create("connection/connectionchanged");
- }
-
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionMessagesExtensions.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectMessagesExtensions.cs
similarity index 100%
rename from src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionMessagesExtensions.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectMessagesExtensions.cs
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionChangedMessages.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionChangedMessages.cs
new file mode 100644
index 00000000..94454bc5
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionChangedMessages.cs
@@ -0,0 +1,36 @@
+//
+// 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.Connection.Contracts
+{
+ ///
+ /// Parameters for the ConnectionChanged Notification.
+ ///
+ public class ConnectionChangedParams
+ {
+ ///
+ /// A URI identifying the owner of the connection. This will most commonly be a file in the workspace
+ /// or a virtual file representing an object in a database.
+ ///
+ public string OwnerUri { get; set; }
+ ///
+ /// Contains the high-level properties about the connection, for display to the user.
+ ///
+ public ConnectionSummary Connection { get; set; }
+ }
+
+ ///
+ /// ConnectionChanged notification mapping entry
+ ///
+ public class ConnectionChangedNotification
+ {
+ public static readonly
+ EventType Type =
+ EventType.Create("connection/connectionchanged");
+ }
+
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/DisconnectMessages.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/DisconnectMessages.cs
new file mode 100644
index 00000000..c078b308
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/DisconnectMessages.cs
@@ -0,0 +1,31 @@
+//
+// 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.Connection.Contracts
+{
+ ///
+ /// Parameters for the Disconnect Request.
+ ///
+ public class DisconnectParams
+ {
+ ///
+ /// A URI identifying the owner of the connection. This will most commonly be a file in the workspace
+ /// or a virtual file representing an object in a database.
+ ///
+ public string OwnerUri { get; set; }
+ }
+
+ ///
+ /// Disconnect request mapping entry
+ ///
+ public class DisconnectRequest
+ {
+ public static readonly
+ RequestType Type =
+ RequestType.Create("connection/disconnect");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteService.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteService.cs
index cb866b2a..14148778 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/AutoCompleteService.cs
@@ -1,325 +1,325 @@
-//
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Data.Common;
-using System.Threading.Tasks;
-using Microsoft.SqlTools.ServiceLayer.Connection;
-using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
-using Microsoft.SqlTools.ServiceLayer.Hosting;
-using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
-using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
-
-namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
-{
- internal class IntellisenseCache
- {
- // connection used to query for intellisense info
- private DbConnection connection;
-
- // number of documents (URI's) that are using the cache for the same database
- // the autocomplete service uses this to remove unreferenced caches
- public int ReferenceCount { get; set; }
-
- public IntellisenseCache(ISqlConnectionFactory connectionFactory, ConnectionDetails connectionDetails)
- {
- ReferenceCount = 0;
- DatabaseInfo = CopySummary(connectionDetails);
-
- // TODO error handling on this. Intellisense should catch or else the service should handle
- connection = connectionFactory.CreateSqlConnection(ConnectionService.BuildConnectionString(connectionDetails));
- connection.Open();
- }
-
- ///
- /// Used to identify a database for which this cache is used
- ///
- public ConnectionSummary DatabaseInfo
- {
- get;
- private set;
- }
- ///
- /// Gets the current autocomplete candidate list
- ///
- public IEnumerable AutoCompleteList { get; private set; }
-
- public async Task UpdateCache()
- {
- DbCommand command = connection.CreateCommand();
- command.CommandText = "SELECT name FROM sys.tables";
- command.CommandTimeout = 15;
- command.CommandType = CommandType.Text;
- var reader = await command.ExecuteReaderAsync();
-
- List results = new List();
- while (await reader.ReadAsync())
- {
- results.Add(reader[0].ToString());
- }
-
- AutoCompleteList = results;
- await Task.FromResult(0);
- }
-
- public List GetAutoCompleteItems(TextDocumentPosition textDocumentPosition)
- {
- List completions = new List();
-
- int i = 0;
-
- // Take a reference to the list at a point in time in case we update and replace the list
- var suggestions = AutoCompleteList;
- // the completion list will be null is user not connected to server
- if (this.AutoCompleteList != null)
- {
-
- foreach (var autoCompleteItem in suggestions)
- {
- // convert the completion item candidates into CompletionItems
- completions.Add(new CompletionItem()
- {
- Label = autoCompleteItem,
- Kind = CompletionItemKind.Keyword,
- Detail = autoCompleteItem + " details",
- Documentation = autoCompleteItem + " documentation",
- TextEdit = new TextEdit
- {
- NewText = autoCompleteItem,
- Range = new Range
- {
- Start = new Position
- {
- Line = textDocumentPosition.Position.Line,
- Character = textDocumentPosition.Position.Character
- },
- End = new Position
- {
- Line = textDocumentPosition.Position.Line,
- Character = textDocumentPosition.Position.Character + 5
- }
- }
- }
- });
-
- // only show 50 items
- if (++i == 50)
- {
- break;
- }
- }
- }
-
- return completions;
- }
-
- private static ConnectionSummary CopySummary(ConnectionSummary summary)
- {
- return new ConnectionSummary()
- {
- ServerName = summary.ServerName,
- DatabaseName = summary.DatabaseName,
- UserName = summary.UserName
- };
- }
- }
-
- ///
- /// Treats connections as the same if their server, db and usernames all match
- ///
- public class ConnectionSummaryComparer : IEqualityComparer
- {
- public bool Equals(ConnectionSummary x, ConnectionSummary y)
- {
- if(x == y) { return true; }
- else if(x != null)
- {
- if(y == null) { return false; }
-
- // Compare server, db, username. Note: server is case-insensitive in the driver
- return string.Compare(x.ServerName, y.ServerName, StringComparison.OrdinalIgnoreCase) == 0
- && string.Compare(x.DatabaseName, y.DatabaseName, StringComparison.Ordinal) == 0
- && string.Compare(x.UserName, y.UserName, StringComparison.Ordinal) == 0;
- }
- return false;
- }
-
- public int GetHashCode(ConnectionSummary obj)
- {
- int hashcode = 31;
- if(obj != null)
- {
- if(obj.ServerName != null)
- {
- hashcode ^= obj.ServerName.GetHashCode();
- }
- if (obj.DatabaseName != null)
- {
- hashcode ^= obj.DatabaseName.GetHashCode();
- }
- if (obj.UserName != null)
- {
- hashcode ^= obj.UserName.GetHashCode();
- }
- }
- return hashcode;
- }
- }
- ///
- /// Main class for Autocomplete functionality
- ///
- public class AutoCompleteService
- {
- #region Singleton Instance Implementation
-
- ///
- /// Singleton service instance
- ///
- private static Lazy instance
- = new Lazy(() => new AutoCompleteService());
-
- ///
- /// Gets the singleton service instance
- ///
- public static AutoCompleteService Instance
- {
- get
- {
- return instance.Value;
- }
- }
-
- ///
- /// Default, parameterless constructor.
- /// TODO: Figure out how to make this truely singleton even with dependency injection for tests
- ///
- public AutoCompleteService()
- {
- }
-
- #endregion
-
- // Dictionary of unique intellisense caches for each Connection
- private Dictionary caches =
- new Dictionary(new ConnectionSummaryComparer());
- private Object cachesLock = new Object(); // Used when we insert/remove something from the cache dictionary
-
- private ISqlConnectionFactory factory;
- private Object factoryLock = new Object();
-
- ///
- /// Internal for testing purposes only
- ///
- internal ISqlConnectionFactory ConnectionFactory
- {
- get
- {
- lock(factoryLock)
- {
- if(factory == null)
- {
- factory = new SqlConnectionFactory();
- }
- }
- return factory;
- }
- set
- {
- lock(factoryLock)
- {
- factory = value;
- }
- }
- }
- public void InitializeService(ServiceHost serviceHost)
- {
- // Register a callback for when a connection is created
- ConnectionService.Instance.RegisterOnConnectionTask(UpdateAutoCompleteCache);
-
- // Register a callback for when a connection is closed
- ConnectionService.Instance.RegisterOnDisconnectTask(RemoveAutoCompleteCacheUriReference);
- }
-
- private async Task UpdateAutoCompleteCache(ConnectionInfo connectionInfo)
- {
- if (connectionInfo != null)
- {
- await UpdateAutoCompleteCache(connectionInfo.ConnectionDetails);
- }
- }
-
- ///
- /// Remove a reference to an autocomplete cache from a URI. If
- /// it is the last URI connected to a particular connection,
- /// then remove the cache.
- ///
- public async Task RemoveAutoCompleteCacheUriReference(ConnectionSummary summary)
- {
- await Task.Run( () =>
- {
- lock(cachesLock)
- {
- IntellisenseCache cache;
- if( caches.TryGetValue(summary, out cache) )
- {
- cache.ReferenceCount--;
-
- // Remove unused caches
- if( cache.ReferenceCount == 0 )
- {
- caches.Remove(summary);
- }
- }
- }
- });
- }
-
-
- ///
- /// Update the cached autocomplete candidate list when the user connects to a database
- ///
- ///
- public async Task UpdateAutoCompleteCache(ConnectionDetails details)
- {
- IntellisenseCache cache;
- lock(cachesLock)
- {
- if(!caches.TryGetValue(details, out cache))
- {
- cache = new IntellisenseCache(ConnectionFactory, details);
- caches[cache.DatabaseInfo] = cache;
- }
- cache.ReferenceCount++;
- }
-
- await cache.UpdateCache();
- }
-
- ///
- /// Return the completion item list for the current text position.
- /// This method does not await cache builds since it expects to return quickly
- ///
- ///
- public CompletionItem[] GetCompletionItems(TextDocumentPosition textDocumentPosition)
- {
- // Try to find a cache for the document's backing connection (if available)
- // If we have a connection but no cache, we don't care - assuming the OnConnect and OnDisconnect listeners
- // behave well, there should be a cache for any actively connected document. This also helps skip documents
- // that are not backed by a SQL connection
- ConnectionInfo info;
- IntellisenseCache cache;
- if (ConnectionService.Instance.TryFindConnection(textDocumentPosition.Uri, out info)
- && caches.TryGetValue((ConnectionSummary)info.ConnectionDetails, out cache))
- {
- return cache.GetAutoCompleteItems(textDocumentPosition).ToArray();
- }
-
- return new CompletionItem[0];
- }
-
- }
-}
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Threading.Tasks;
+using Microsoft.SqlTools.ServiceLayer.Connection;
+using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Hosting;
+using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
+{
+ internal class IntellisenseCache
+ {
+ // connection used to query for intellisense info
+ private DbConnection connection;
+
+ // number of documents (URI's) that are using the cache for the same database
+ // the autocomplete service uses this to remove unreferenced caches
+ public int ReferenceCount { get; set; }
+
+ public IntellisenseCache(ISqlConnectionFactory connectionFactory, ConnectionDetails connectionDetails)
+ {
+ ReferenceCount = 0;
+ DatabaseInfo = CopySummary(connectionDetails);
+
+ // TODO error handling on this. Intellisense should catch or else the service should handle
+ connection = connectionFactory.CreateSqlConnection(ConnectionService.BuildConnectionString(connectionDetails));
+ connection.Open();
+ }
+
+ ///
+ /// Used to identify a database for which this cache is used
+ ///
+ public ConnectionSummary DatabaseInfo
+ {
+ get;
+ private set;
+ }
+ ///
+ /// Gets the current autocomplete candidate list
+ ///
+ public IEnumerable AutoCompleteList { get; private set; }
+
+ public async Task UpdateCache()
+ {
+ DbCommand command = connection.CreateCommand();
+ command.CommandText = "SELECT name FROM sys.tables";
+ command.CommandTimeout = 15;
+ command.CommandType = CommandType.Text;
+ var reader = await command.ExecuteReaderAsync();
+
+ List results = new List();
+ while (await reader.ReadAsync())
+ {
+ results.Add(reader[0].ToString());
+ }
+
+ AutoCompleteList = results;
+ await Task.FromResult(0);
+ }
+
+ public List GetAutoCompleteItems(TextDocumentPosition textDocumentPosition)
+ {
+ List completions = new List();
+
+ int i = 0;
+
+ // Take a reference to the list at a point in time in case we update and replace the list
+ var suggestions = AutoCompleteList;
+ // the completion list will be null is user not connected to server
+ if (this.AutoCompleteList != null)
+ {
+
+ foreach (var autoCompleteItem in suggestions)
+ {
+ // convert the completion item candidates into CompletionItems
+ completions.Add(new CompletionItem()
+ {
+ Label = autoCompleteItem,
+ Kind = CompletionItemKind.Keyword,
+ Detail = autoCompleteItem + " details",
+ Documentation = autoCompleteItem + " documentation",
+ TextEdit = new TextEdit
+ {
+ NewText = autoCompleteItem,
+ Range = new Range
+ {
+ Start = new Position
+ {
+ Line = textDocumentPosition.Position.Line,
+ Character = textDocumentPosition.Position.Character
+ },
+ End = new Position
+ {
+ Line = textDocumentPosition.Position.Line,
+ Character = textDocumentPosition.Position.Character + 5
+ }
+ }
+ }
+ });
+
+ // only show 50 items
+ if (++i == 50)
+ {
+ break;
+ }
+ }
+ }
+
+ return completions;
+ }
+
+ private static ConnectionSummary CopySummary(ConnectionSummary summary)
+ {
+ return new ConnectionSummary()
+ {
+ ServerName = summary.ServerName,
+ DatabaseName = summary.DatabaseName,
+ UserName = summary.UserName
+ };
+ }
+ }
+
+ ///
+ /// Treats connections as the same if their server, db and usernames all match
+ ///
+ public class ConnectionSummaryComparer : IEqualityComparer
+ {
+ public bool Equals(ConnectionSummary x, ConnectionSummary y)
+ {
+ if(x == y) { return true; }
+ else if(x != null)
+ {
+ if(y == null) { return false; }
+
+ // Compare server, db, username. Note: server is case-insensitive in the driver
+ return string.Compare(x.ServerName, y.ServerName, StringComparison.OrdinalIgnoreCase) == 0
+ && string.Compare(x.DatabaseName, y.DatabaseName, StringComparison.Ordinal) == 0
+ && string.Compare(x.UserName, y.UserName, StringComparison.Ordinal) == 0;
+ }
+ return false;
+ }
+
+ public int GetHashCode(ConnectionSummary obj)
+ {
+ int hashcode = 31;
+ if(obj != null)
+ {
+ if(obj.ServerName != null)
+ {
+ hashcode ^= obj.ServerName.GetHashCode();
+ }
+ if (obj.DatabaseName != null)
+ {
+ hashcode ^= obj.DatabaseName.GetHashCode();
+ }
+ if (obj.UserName != null)
+ {
+ hashcode ^= obj.UserName.GetHashCode();
+ }
+ }
+ return hashcode;
+ }
+ }
+ ///
+ /// Main class for Autocomplete functionality
+ ///
+ public class AutoCompleteService
+ {
+ #region Singleton Instance Implementation
+
+ ///
+ /// Singleton service instance
+ ///
+ private static Lazy instance
+ = new Lazy(() => new AutoCompleteService());
+
+ ///
+ /// Gets the singleton service instance
+ ///
+ public static AutoCompleteService Instance
+ {
+ get
+ {
+ return instance.Value;
+ }
+ }
+
+ ///
+ /// Default, parameterless constructor.
+ /// TODO: Figure out how to make this truely singleton even with dependency injection for tests
+ ///
+ public AutoCompleteService()
+ {
+ }
+
+ #endregion
+
+ // Dictionary of unique intellisense caches for each Connection
+ private Dictionary caches =
+ new Dictionary(new ConnectionSummaryComparer());
+ private Object cachesLock = new Object(); // Used when we insert/remove something from the cache dictionary
+
+ private ISqlConnectionFactory factory;
+ private Object factoryLock = new Object();
+
+ ///
+ /// Internal for testing purposes only
+ ///
+ internal ISqlConnectionFactory ConnectionFactory
+ {
+ get
+ {
+ lock(factoryLock)
+ {
+ if(factory == null)
+ {
+ factory = new SqlConnectionFactory();
+ }
+ }
+ return factory;
+ }
+ set
+ {
+ lock(factoryLock)
+ {
+ factory = value;
+ }
+ }
+ }
+ public void InitializeService(ServiceHost serviceHost)
+ {
+ // Register a callback for when a connection is created
+ ConnectionService.Instance.RegisterOnConnectionTask(UpdateAutoCompleteCache);
+
+ // Register a callback for when a connection is closed
+ ConnectionService.Instance.RegisterOnDisconnectTask(RemoveAutoCompleteCacheUriReference);
+ }
+
+ private async Task UpdateAutoCompleteCache(ConnectionInfo connectionInfo)
+ {
+ if (connectionInfo != null)
+ {
+ await UpdateAutoCompleteCache(connectionInfo.ConnectionDetails);
+ }
+ }
+
+ ///
+ /// Remove a reference to an autocomplete cache from a URI. If
+ /// it is the last URI connected to a particular connection,
+ /// then remove the cache.
+ ///
+ public async Task RemoveAutoCompleteCacheUriReference(ConnectionSummary summary)
+ {
+ await Task.Run( () =>
+ {
+ lock(cachesLock)
+ {
+ IntellisenseCache cache;
+ if( caches.TryGetValue(summary, out cache) )
+ {
+ cache.ReferenceCount--;
+
+ // Remove unused caches
+ if( cache.ReferenceCount == 0 )
+ {
+ caches.Remove(summary);
+ }
+ }
+ }
+ });
+ }
+
+
+ ///
+ /// Update the cached autocomplete candidate list when the user connects to a database
+ ///
+ ///
+ public async Task UpdateAutoCompleteCache(ConnectionDetails details)
+ {
+ IntellisenseCache cache;
+ lock(cachesLock)
+ {
+ if(!caches.TryGetValue(details, out cache))
+ {
+ cache = new IntellisenseCache(ConnectionFactory, details);
+ caches[cache.DatabaseInfo] = cache;
+ }
+ cache.ReferenceCount++;
+ }
+
+ await cache.UpdateCache();
+ }
+
+ ///
+ /// Return the completion item list for the current text position.
+ /// This method does not await cache builds since it expects to return quickly
+ ///
+ ///
+ public CompletionItem[] GetCompletionItems(TextDocumentPosition textDocumentPosition)
+ {
+ // Try to find a cache for the document's backing connection (if available)
+ // If we have a connection but no cache, we don't care - assuming the OnConnect and OnDisconnect listeners
+ // behave well, there should be a cache for any actively connected document. This also helps skip documents
+ // that are not backed by a SQL connection
+ ConnectionInfo info;
+ IntellisenseCache cache;
+ if (ConnectionService.Instance.TryFindConnection(textDocumentPosition.Uri, out info)
+ && caches.TryGetValue((ConnectionSummary)info.ConnectionDetails, out cache))
+ {
+ return cache.GetAutoCompleteItems(textDocumentPosition).ToArray();
+ }
+
+ return new CompletionItem[0];
+ }
+
+ }
+}