From f3231fba56bc2f0a5edefcd58e7a496a6735d410 Mon Sep 17 00:00:00 2001 From: Mitchell Sternke Date: Tue, 9 Aug 2016 10:17:29 -0700 Subject: [PATCH] Addressing PR 14 feedback --- nuget.config | 1 - .../Connection/ConnectionService.cs | 19 +- ...nnectionMessages.cs => ConnectMessages.cs} | 62 +- ...nsions.cs => ConnectMessagesExtensions.cs} | 0 .../Contracts/ConnectionChangedMessages.cs | 36 + .../Contracts/DisconnectMessages.cs | 31 + .../LanguageServices/AutoCompleteService.cs | 650 +++++++++--------- 7 files changed, 406 insertions(+), 393 deletions(-) rename src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/{ConnectionMessages.cs => ConnectMessages.cs} (63%) rename src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/{ConnectionMessagesExtensions.cs => ConnectMessagesExtensions.cs} (100%) create mode 100644 src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionChangedMessages.cs create mode 100644 src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/DisconnectMessages.cs 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]; + } + + } +}