mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Adding completion extension loading and execution logic (#833)
* Adding ICompletionExtension interface * Adding extension loading and execution logic * Fixing compilation error in VS 2017 * Using MEF for completion extension discovery * using await on GetCompletionItems * Adding an integration test for completion extension and update the completion extension interface * Update the completion extension test * Fix issues based on review comments * Remove try/cache based on review comments, fix a integration test. * More changes based on review comments * Fixing SendResult logic for completion extension loading * Only load completion extension from the assembly passed in, add more comments in the test * Adding right assert messages in the test. * More fixes based on review comments * Dropping ICompletionExtensionProvider, load assembly only if it's loaded at the first time or updated since last load. * Fix based on the latest review comments * Adding missing TSQL functions in default completion list * Update jsonrpc documentation for completion/extLoad
This commit is contained in:
@@ -66,6 +66,10 @@ The SQL Tools Service implements the following portion Language Service Protocol
|
|||||||
* :leftwards_arrow_with_hook: [textDocument/references](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_references)
|
* :leftwards_arrow_with_hook: [textDocument/references](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_references)
|
||||||
* :leftwards_arrow_with_hook: [textDocument/definition](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_definition)
|
* :leftwards_arrow_with_hook: [textDocument/definition](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_definition)
|
||||||
|
|
||||||
|
### Language Service Protocol Extensions
|
||||||
|
|
||||||
|
* :leftwards_arrow_with_hook: [completion/extLoad](#completion_extLoad)
|
||||||
|
|
||||||
# Message Protocol
|
# Message Protocol
|
||||||
|
|
||||||
A message consists of two parts: a header section and the message body. For now, there is
|
A message consists of two parts: a header section and the message body. For now, there is
|
||||||
@@ -450,7 +454,7 @@ Disconnect the connection specified in the request.
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A URI identifying the owner of the connection. This will most commonly be a file in the workspace
|
/// 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.
|
/// or a virtual file representing an object in a database.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string OwnerUri { get; set; }
|
public string OwnerUri { get; set; }
|
||||||
}
|
}
|
||||||
@@ -619,7 +623,7 @@ Save a resultset as CSV to a file.
|
|||||||
/// End index of the selected rows (inclusive)
|
/// End index of the selected rows (inclusive)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? RowEndIndex { get; set; }
|
public int? RowEndIndex { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start index of the selected columns (inclusive)
|
/// Start index of the selected columns (inclusive)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -661,7 +665,7 @@ Save a resultset as CSV to a file.
|
|||||||
public class SaveResultRequestResult
|
public class SaveResultRequestResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Error messages for saving to file.
|
/// Error messages for saving to file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Messages { get; set; }
|
public string Messages { get; set; }
|
||||||
}
|
}
|
||||||
@@ -705,7 +709,7 @@ Save a resultset as JSON to a file.
|
|||||||
/// End index of the selected rows (inclusive)
|
/// End index of the selected rows (inclusive)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? RowEndIndex { get; set; }
|
public int? RowEndIndex { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start index of the selected columns (inclusive)
|
/// Start index of the selected columns (inclusive)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -739,8 +743,42 @@ Save a resultset as JSON to a file.
|
|||||||
public class SaveResultRequestResult
|
public class SaveResultRequestResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Error messages for saving to file.
|
/// Error messages for saving to file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Messages { get; set; }
|
public string Messages { get; set; }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Language Service Protocol Extensions
|
||||||
|
|
||||||
|
### <a name="completion_extload"></a>`completion/extLoad`
|
||||||
|
|
||||||
|
Load a completion extension.
|
||||||
|
|
||||||
|
#### Request
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
public class CompletionExtensionParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Absolute path for the assembly containing the completion extension
|
||||||
|
/// </summary>
|
||||||
|
public string AssemblyPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type name for the completion extension
|
||||||
|
/// </summary>
|
||||||
|
public string TypeName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Property bag for initializing the completion extension
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, object> Properties { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Response
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
bool
|
||||||
|
```
|
||||||
@@ -95,6 +95,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.ManagedB
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.ManagedBatchParser.IntegrationTests", "test\Microsoft.SqlTools.ManagedBatchParser.IntegrationTests\Microsoft.SqlTools.ManagedBatchParser.IntegrationTests.csproj", "{D3696EFA-FB1E-4848-A726-FF7B168AFB96}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.ManagedBatchParser.IntegrationTests", "test\Microsoft.SqlTools.ManagedBatchParser.IntegrationTests\Microsoft.SqlTools.ManagedBatchParser.IntegrationTests.csproj", "{D3696EFA-FB1E-4848-A726-FF7B168AFB96}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.Test.CompletionExtension", "test\CompletionExtSample\Microsoft.SqlTools.Test.CompletionExtension.csproj", "{0EC2B30C-0652-49AE-9594-85B3C3E9CA21}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -228,6 +230,12 @@ Global
|
|||||||
{D3696EFA-FB1E-4848-A726-FF7B168AFB96}.Integration|Any CPU.Build.0 = Debug|Any CPU
|
{D3696EFA-FB1E-4848-A726-FF7B168AFB96}.Integration|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D3696EFA-FB1E-4848-A726-FF7B168AFB96}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{D3696EFA-FB1E-4848-A726-FF7B168AFB96}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{D3696EFA-FB1E-4848-A726-FF7B168AFB96}.Release|Any CPU.Build.0 = Release|Any CPU
|
{D3696EFA-FB1E-4848-A726-FF7B168AFB96}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0EC2B30C-0652-49AE-9594-85B3C3E9CA21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0EC2B30C-0652-49AE-9594-85B3C3E9CA21}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0EC2B30C-0652-49AE-9594-85B3C3E9CA21}.Integration|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0EC2B30C-0652-49AE-9594-85B3C3E9CA21}.Integration|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0EC2B30C-0652-49AE-9594-85B3C3E9CA21}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0EC2B30C-0652-49AE-9594-85B3C3E9CA21}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -256,6 +264,7 @@ Global
|
|||||||
{EF02F89F-417E-4A40-B7E6-B102EE2DF24D} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
|
{EF02F89F-417E-4A40-B7E6-B102EE2DF24D} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
|
||||||
{3F82F298-700A-48DF-8A69-D048DFBA782C} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
|
{3F82F298-700A-48DF-8A69-D048DFBA782C} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
|
||||||
{D3696EFA-FB1E-4848-A726-FF7B168AFB96} = {AB9CA2B8-6F70-431C-8A1D-67479D8A7BE4}
|
{D3696EFA-FB1E-4848-A726-FF7B168AFB96} = {AB9CA2B8-6F70-431C-8A1D-67479D8A7BE4}
|
||||||
|
{0EC2B30C-0652-49AE-9594-85B3C3E9CA21} = {AB9CA2B8-6F70-431C-8A1D-67479D8A7BE4}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {B31CDF4B-2851-45E5-8C5F-BE97125D9DD8}
|
SolutionGuid = {B31CDF4B-2851-45E5-8C5F-BE97125D9DD8}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Composition.Convention;
|
using System.Composition.Convention;
|
||||||
using System.Composition.Hosting;
|
using System.Composition.Hosting;
|
||||||
@@ -17,19 +18,19 @@ using Microsoft.SqlTools.Hosting.Utility;
|
|||||||
namespace Microsoft.SqlTools.Hosting.Extensibility
|
namespace Microsoft.SqlTools.Hosting.Extensibility
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A MEF-based service provider. Supports any MEF-based configuration but is optimized for
|
/// A MEF-based service provider. Supports any MEF-based configuration but is optimized for
|
||||||
/// service discovery over a set of DLLs in an application scope. Any service registering using
|
/// service discovery over a set of DLLs in an application scope. Any service registering using
|
||||||
/// the <c>[Export(IServiceContract)]</c> attribute will be discovered and used by this service
|
/// the <c>[Export(IServiceContract)]</c> attribute will be discovered and used by this service
|
||||||
/// provider if it's in the set of Assemblies / Types specified during its construction. Manual
|
/// provider if it's in the set of Assemblies / Types specified during its construction. Manual
|
||||||
/// override of this is supported by calling
|
/// override of this is supported by calling
|
||||||
/// <see cref="RegisteredServiceProvider.RegisterSingleService" /> and similar methods, since
|
/// <see cref="RegisteredServiceProvider.RegisterSingleService" /> and similar methods, since
|
||||||
/// this will initialize that service contract and avoid the MEF-based search and discovery
|
/// this will initialize that service contract and avoid the MEF-based search and discovery
|
||||||
/// process. This allows the service provider to link into existing singleton / known services
|
/// process. This allows the service provider to link into existing singleton / known services
|
||||||
/// while using MEF-based dependency injection and inversion of control for most of the code.
|
/// while using MEF-based dependency injection and inversion of control for most of the code.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ExtensionServiceProvider : RegisteredServiceProvider
|
public class ExtensionServiceProvider : RegisteredServiceProvider
|
||||||
{
|
{
|
||||||
private readonly Func<ConventionBuilder, ContainerConfiguration> config;
|
private Func<ConventionBuilder, ContainerConfiguration> config;
|
||||||
|
|
||||||
public ExtensionServiceProvider(Func<ConventionBuilder, ContainerConfiguration> config)
|
public ExtensionServiceProvider(Func<ConventionBuilder, ContainerConfiguration> config)
|
||||||
{
|
{
|
||||||
@@ -106,8 +107,23 @@ namespace Microsoft.SqlTools.Hosting.Extensibility
|
|||||||
Register(() => store.GetExports<T>());
|
Register(() => store.GetExports<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Merges in new assemblies to the existing container configuration.
|
||||||
|
/// </summary>
|
||||||
|
public void AddAssembliesToConfiguration(IEnumerable<Assembly> assemblies)
|
||||||
|
{
|
||||||
|
Validate.IsNotNull(nameof(assemblies), assemblies);
|
||||||
|
var previousConfig = config;
|
||||||
|
this.config = conventions => {
|
||||||
|
// Chain in the existing configuration function's result, then include additional
|
||||||
|
// assemblies
|
||||||
|
ContainerConfiguration containerConfig = previousConfig(conventions);
|
||||||
|
return containerConfig.WithAssemblies(assemblies, conventions);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A store for MEF exports of a specific type. Provides basic wrapper functionality around MEF to standarize how
|
/// A store for MEF exports of a specific type. Provides basic wrapper functionality around MEF to standarize how
|
||||||
/// we lookup types and return to callers.
|
/// we lookup types and return to callers.
|
||||||
@@ -117,7 +133,7 @@ namespace Microsoft.SqlTools.Hosting.Extensibility
|
|||||||
private readonly CompositionHost host;
|
private readonly CompositionHost host;
|
||||||
private IList exports;
|
private IList exports;
|
||||||
private readonly Type contractType;
|
private readonly Type contractType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the store with a type to lookup exports of, and a function that configures the
|
/// Initializes the store with a type to lookup exports of, and a function that configures the
|
||||||
/// lookup parameters.
|
/// lookup parameters.
|
||||||
@@ -142,7 +158,7 @@ namespace Microsoft.SqlTools.Hosting.Extensibility
|
|||||||
}
|
}
|
||||||
return exports.Cast<T>();
|
return exports.Cast<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConventionBuilder GetExportBuilder()
|
private ConventionBuilder GetExportBuilder()
|
||||||
{
|
{
|
||||||
// Define exports as matching a parent type, export as that parent type
|
// Define exports as matching a parent type, export as that parent type
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Composition.Convention;
|
using System.Composition.Convention;
|
||||||
using System.Composition.Hosting;
|
using System.Composition.Hosting;
|
||||||
@@ -19,7 +20,7 @@ namespace Microsoft.SqlTools.Extensibility
|
|||||||
{
|
{
|
||||||
public class ExtensionServiceProvider : RegisteredServiceProvider
|
public class ExtensionServiceProvider : RegisteredServiceProvider
|
||||||
{
|
{
|
||||||
private static readonly string[] defaultInclusionList =
|
private static readonly string[] defaultInclusionList =
|
||||||
{
|
{
|
||||||
"microsofsqltoolscredentials.dll",
|
"microsofsqltoolscredentials.dll",
|
||||||
"microsoft.sqltools.hosting.dll",
|
"microsoft.sqltools.hosting.dll",
|
||||||
@@ -36,8 +37,8 @@ namespace Microsoft.SqlTools.Extensibility
|
|||||||
|
|
||||||
public static ExtensionServiceProvider CreateDefaultServiceProvider(string[] inclusionList = null)
|
public static ExtensionServiceProvider CreateDefaultServiceProvider(string[] inclusionList = null)
|
||||||
{
|
{
|
||||||
// only allow loading MEF dependencies from our assemblies until we can
|
// only allow loading MEF dependencies from our assemblies until we can
|
||||||
// better seperate out framework assemblies and extension assemblies
|
// better seperate out framework assemblies and extension assemblies
|
||||||
return CreateFromAssembliesInDirectory(inclusionList ?? defaultInclusionList);
|
return CreateFromAssembliesInDirectory(inclusionList ?? defaultInclusionList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,8 +115,24 @@ namespace Microsoft.SqlTools.Extensibility
|
|||||||
base.Register<T>(() => store.GetExports<T>());
|
base.Register<T>(() => store.GetExports<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Merges in new assemblies to the existing container configuration.
|
||||||
|
/// </summary>
|
||||||
|
public void AddAssembliesToConfiguration(IEnumerable<Assembly> assemblies)
|
||||||
|
{
|
||||||
|
Validate.IsNotNull(nameof(assemblies), assemblies);
|
||||||
|
var previousConfig = config;
|
||||||
|
this.config = conventions => {
|
||||||
|
// Chain in the existing configuration function's result, then include additional
|
||||||
|
// assemblies
|
||||||
|
ContainerConfiguration containerConfig = previousConfig(conventions);
|
||||||
|
return containerConfig.WithAssemblies(assemblies, conventions);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A store for MEF exports of a specific type. Provides basic wrapper functionality around MEF to standarize how
|
/// A store for MEF exports of a specific type. Provides basic wrapper functionality around MEF to standarize how
|
||||||
/// we lookup types and return to callers.
|
/// we lookup types and return to callers.
|
||||||
@@ -125,7 +142,7 @@ namespace Microsoft.SqlTools.Extensibility
|
|||||||
private CompositionHost host;
|
private CompositionHost host;
|
||||||
private IList exports;
|
private IList exports;
|
||||||
private Type contractType;
|
private Type contractType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the store with a type to lookup exports of, and a function that configures the
|
/// Initializes the store with a type to lookup exports of, and a function that configures the
|
||||||
/// lookup parameters.
|
/// lookup parameters.
|
||||||
@@ -150,7 +167,7 @@ namespace Microsoft.SqlTools.Extensibility
|
|||||||
{
|
{
|
||||||
return CreateAssemblyStore<T>(typeof(ExtensionStore).GetTypeInfo().Assembly);
|
return CreateAssemblyStore<T>(typeof(ExtensionStore).GetTypeInfo().Assembly);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ExtensionStore CreateAssemblyStore<T>(Assembly assembly)
|
public static ExtensionStore CreateAssemblyStore<T>(Assembly assembly)
|
||||||
{
|
{
|
||||||
Validate.IsNotNull(nameof(assembly), assembly);
|
Validate.IsNotNull(nameof(assembly), assembly);
|
||||||
@@ -162,7 +179,7 @@ namespace Microsoft.SqlTools.Extensibility
|
|||||||
{
|
{
|
||||||
string assemblyPath = typeof(ExtensionStore).GetTypeInfo().Assembly.Location;
|
string assemblyPath = typeof(ExtensionStore).GetTypeInfo().Assembly.Location;
|
||||||
string directory = Path.GetDirectoryName(assemblyPath);
|
string directory = Path.GetDirectoryName(assemblyPath);
|
||||||
return new ExtensionStore(typeof(T), (conventions) =>
|
return new ExtensionStore(typeof(T), (conventions) =>
|
||||||
new ContainerConfiguration().WithAssembliesInPath(directory, conventions));
|
new ContainerConfiguration().WithAssembliesInPath(directory, conventions));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +191,7 @@ namespace Microsoft.SqlTools.Extensibility
|
|||||||
}
|
}
|
||||||
return exports.Cast<T>();
|
return exports.Cast<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConventionBuilder GetExportBuilder()
|
private ConventionBuilder GetExportBuilder()
|
||||||
{
|
{
|
||||||
// Define exports as matching a parent type, export as that parent type
|
// Define exports as matching a parent type, export as that parent type
|
||||||
@@ -183,7 +200,7 @@ namespace Microsoft.SqlTools.Extensibility
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ContainerConfigurationExtensions
|
public static class ContainerConfigurationExtensions
|
||||||
{
|
{
|
||||||
public static ContainerConfiguration WithAssembliesInPath(this ContainerConfiguration configuration, string path, SearchOption searchOption = SearchOption.TopDirectoryOnly)
|
public static ContainerConfiguration WithAssembliesInPath(this ContainerConfiguration configuration, string path, SearchOption searchOption = SearchOption.TopDirectoryOnly)
|
||||||
@@ -230,7 +247,7 @@ namespace Microsoft.SqlTools.Extensibility
|
|||||||
var apiApplicationFileInfo = new FileInfo($"{folderPath}{Path.DirectorySeparatorChar}{assemblyName.Name}.dll");
|
var apiApplicationFileInfo = new FileInfo($"{folderPath}{Path.DirectorySeparatorChar}{assemblyName.Name}.dll");
|
||||||
if (File.Exists(apiApplicationFileInfo.FullName))
|
if (File.Exists(apiApplicationFileInfo.FullName))
|
||||||
{
|
{
|
||||||
// Creating a new AssemblyContext instance for the same folder puts us at risk
|
// Creating a new AssemblyContext instance for the same folder puts us at risk
|
||||||
// of loading the same DLL in multiple contexts, which leads to some unpredictable
|
// of loading the same DLL in multiple contexts, which leads to some unpredictable
|
||||||
// behavior in the loader. See https://github.com/dotnet/coreclr/issues/19632
|
// behavior in the loader. See https://github.com/dotnet/coreclr/issues/19632
|
||||||
|
|
||||||
|
|||||||
@@ -29,305 +29,394 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
private static CompletionItem[] emptyCompletionList = new CompletionItem[0];
|
private static CompletionItem[] emptyCompletionList = new CompletionItem[0];
|
||||||
|
|
||||||
private static readonly string[] DefaultCompletionText = new string[]
|
private static readonly string[] DefaultCompletionText = new string[]
|
||||||
{
|
{
|
||||||
"all",
|
"abs",
|
||||||
"alter",
|
"acos",
|
||||||
"and",
|
"all",
|
||||||
"apply",
|
"alter",
|
||||||
"as",
|
"and",
|
||||||
"asc",
|
"apply",
|
||||||
"at",
|
"approx_count_distinct",
|
||||||
"backup",
|
"as",
|
||||||
"begin",
|
"asc",
|
||||||
"between",
|
"ascii",
|
||||||
"binary",
|
"asin",
|
||||||
"bit",
|
"at",
|
||||||
"break",
|
"atan",
|
||||||
"bulk",
|
"atn2",
|
||||||
"by",
|
"avg",
|
||||||
"call",
|
"backup",
|
||||||
"cascade",
|
"begin",
|
||||||
"case",
|
"between",
|
||||||
"catch",
|
"binary",
|
||||||
"char",
|
"bit",
|
||||||
"character",
|
"break",
|
||||||
"check",
|
"bulk",
|
||||||
"checkpoint",
|
"by",
|
||||||
"close",
|
"call",
|
||||||
"clustered",
|
"cascade",
|
||||||
"column",
|
"case",
|
||||||
"columnstore",
|
"cast",
|
||||||
"commit",
|
"catch",
|
||||||
"connect",
|
"ceiling",
|
||||||
"constraint",
|
"char",
|
||||||
"continue",
|
"character",
|
||||||
"create",
|
"charindex",
|
||||||
"cross",
|
"check",
|
||||||
"current_date",
|
"checkpoint",
|
||||||
"cursor",
|
"checksum_agg",
|
||||||
"cursor_close_on_commit",
|
"close",
|
||||||
"cursor_default",
|
"clustered",
|
||||||
"data",
|
"coalesce",
|
||||||
"data_compression",
|
"column",
|
||||||
"database",
|
"columnstore",
|
||||||
"date",
|
"commit",
|
||||||
"datetime",
|
"concat",
|
||||||
"datetime2",
|
"concat_ws",
|
||||||
"days",
|
"connect",
|
||||||
"dbcc",
|
"constraint",
|
||||||
"dec",
|
"continue",
|
||||||
"decimal",
|
"convert",
|
||||||
"declare",
|
"cos",
|
||||||
"default",
|
"cot",
|
||||||
"delete",
|
"count",
|
||||||
"deny",
|
"count_big",
|
||||||
"desc",
|
"create",
|
||||||
"description",
|
"cross",
|
||||||
"disabled",
|
"current_date",
|
||||||
"disk",
|
"current_timestamp",
|
||||||
"distinct",
|
"current_user",
|
||||||
"double",
|
"cursor",
|
||||||
"drop",
|
"cursor_close_on_commit",
|
||||||
"drop_existing",
|
"cursor_default",
|
||||||
"dump",
|
"data",
|
||||||
"dynamic",
|
"data_compression",
|
||||||
"else",
|
"database",
|
||||||
"enable",
|
"datalength",
|
||||||
"encrypted",
|
"date",
|
||||||
"end",
|
"dateadd",
|
||||||
"end-exec",
|
"datediff",
|
||||||
"except",
|
"datefromparts",
|
||||||
"exec",
|
"datename",
|
||||||
"execute",
|
"datepart",
|
||||||
"exists",
|
"datetime",
|
||||||
"exit",
|
"datetime2",
|
||||||
"external",
|
"day",
|
||||||
"fast_forward",
|
"days",
|
||||||
"fetch",
|
"dbcc",
|
||||||
"file",
|
"dec",
|
||||||
"filegroup",
|
"decimal",
|
||||||
"filename",
|
"declare",
|
||||||
"filestream",
|
"default",
|
||||||
"filter",
|
"degrees",
|
||||||
"first",
|
"delete",
|
||||||
"float",
|
"deny",
|
||||||
"for",
|
"desc",
|
||||||
"foreign",
|
"description",
|
||||||
"from",
|
"difference",
|
||||||
"full",
|
"disabled",
|
||||||
"function",
|
"disk",
|
||||||
"geography",
|
"distinct",
|
||||||
"get",
|
"double",
|
||||||
"global",
|
"drop",
|
||||||
"go",
|
"drop_existing",
|
||||||
"goto",
|
"dump",
|
||||||
"grant",
|
"dynamic",
|
||||||
"group",
|
"else",
|
||||||
"hash",
|
"enable",
|
||||||
"hashed",
|
"encrypted",
|
||||||
"having",
|
"end",
|
||||||
"hidden",
|
"end-exec",
|
||||||
"hierarchyid",
|
"except",
|
||||||
"holdlock",
|
"exec",
|
||||||
"hours",
|
"execute",
|
||||||
"identity",
|
"exists",
|
||||||
"identitycol",
|
"exit",
|
||||||
"if",
|
"exp",
|
||||||
"image",
|
"external",
|
||||||
"immediate",
|
"fast_forward",
|
||||||
"include",
|
"fetch",
|
||||||
"index",
|
"file",
|
||||||
"inner",
|
"filegroup",
|
||||||
"insert",
|
"filename",
|
||||||
"instead",
|
"filestream",
|
||||||
"int",
|
"filter",
|
||||||
"integer",
|
"first",
|
||||||
"intersect",
|
"float",
|
||||||
"into",
|
"floor",
|
||||||
"isolation",
|
"for",
|
||||||
"join",
|
"foreign",
|
||||||
"json",
|
"format",
|
||||||
"key",
|
"from",
|
||||||
"language",
|
"full",
|
||||||
"last",
|
"function",
|
||||||
"left",
|
"geography",
|
||||||
"level",
|
"get",
|
||||||
"lineno",
|
"getdate",
|
||||||
"load",
|
"getutcdate",
|
||||||
"local",
|
"global",
|
||||||
"locate",
|
"go",
|
||||||
"location",
|
"goto",
|
||||||
"login",
|
"grant",
|
||||||
"masked",
|
"group",
|
||||||
"maxdop",
|
"grouping",
|
||||||
"merge",
|
"grouping_id",
|
||||||
"message",
|
"hash",
|
||||||
"modify",
|
"hashed",
|
||||||
"move",
|
"having",
|
||||||
"namespace",
|
"hidden",
|
||||||
"native_compilation",
|
"hierarchyid",
|
||||||
"nchar",
|
"holdlock",
|
||||||
"next",
|
"hours",
|
||||||
"no",
|
"identity",
|
||||||
"nocheck",
|
"identitycol",
|
||||||
"nocount",
|
"if",
|
||||||
"nonclustered",
|
"iif",
|
||||||
"none",
|
"image",
|
||||||
"norecompute",
|
"immediate",
|
||||||
"not",
|
"include",
|
||||||
"now",
|
"index",
|
||||||
"null",
|
"inner",
|
||||||
"numeric",
|
"insert",
|
||||||
"nvarchar",
|
"instead",
|
||||||
"object",
|
"int",
|
||||||
"of",
|
"integer",
|
||||||
"off",
|
"intersect",
|
||||||
"offsets",
|
"into",
|
||||||
"on",
|
"isdate",
|
||||||
"online",
|
"isnull",
|
||||||
"open",
|
"isnumeric",
|
||||||
"openrowset",
|
"isolation",
|
||||||
"openxml",
|
"join",
|
||||||
"option",
|
"json",
|
||||||
"or",
|
"key",
|
||||||
"order",
|
"language",
|
||||||
"out",
|
"last",
|
||||||
"outer",
|
"left",
|
||||||
"output",
|
"len",
|
||||||
"over",
|
"level",
|
||||||
"owner",
|
"lineno",
|
||||||
"partial",
|
"load",
|
||||||
"partition",
|
"local",
|
||||||
"password",
|
"locate",
|
||||||
"path",
|
"location",
|
||||||
"percent",
|
"log",
|
||||||
"percentage",
|
"log10",
|
||||||
"period",
|
"login",
|
||||||
"persisted",
|
"lower",
|
||||||
"plan",
|
"ltrim",
|
||||||
"policy",
|
"masked",
|
||||||
"precision",
|
"max",
|
||||||
"predicate",
|
"maxdop",
|
||||||
"primary",
|
"merge",
|
||||||
"print",
|
"message",
|
||||||
"prior",
|
"min",
|
||||||
"proc",
|
"modify",
|
||||||
"procedure",
|
"month",
|
||||||
"public",
|
"move",
|
||||||
"query_store",
|
"namespace",
|
||||||
"quoted_identifier",
|
"native_compilation",
|
||||||
"raiserror",
|
"nchar",
|
||||||
"range",
|
"next",
|
||||||
"raw",
|
"no",
|
||||||
"read",
|
"nocheck",
|
||||||
"read_committed_snapshot",
|
"nocount",
|
||||||
"read_only",
|
"nonclustered",
|
||||||
"read_write",
|
"none",
|
||||||
"readonly",
|
"norecompute",
|
||||||
"readtext",
|
"not",
|
||||||
"real",
|
"now",
|
||||||
"rebuild",
|
"null",
|
||||||
"receive",
|
"nullif",
|
||||||
"reconfigure",
|
"numeric",
|
||||||
"recovery",
|
"nvarchar",
|
||||||
"recursive",
|
"object",
|
||||||
"recursive_triggers",
|
"of",
|
||||||
"references",
|
"off",
|
||||||
"relative",
|
"offsets",
|
||||||
"remove",
|
"on",
|
||||||
"reorganize",
|
"online",
|
||||||
"required",
|
"open",
|
||||||
"restart",
|
"openrowset",
|
||||||
"restore",
|
"openxml",
|
||||||
"restrict",
|
"option",
|
||||||
"resume",
|
"or",
|
||||||
"return",
|
"order",
|
||||||
"returns",
|
"out",
|
||||||
"revert",
|
"outer",
|
||||||
"revoke",
|
"output",
|
||||||
"rollback",
|
"over",
|
||||||
"rollup",
|
"owner",
|
||||||
"row",
|
"partial",
|
||||||
"rowcount",
|
"partition",
|
||||||
"rowguidcol",
|
"password",
|
||||||
"rows",
|
"path",
|
||||||
"rule",
|
"patindex",
|
||||||
"sample",
|
"percent",
|
||||||
"save",
|
"percentage",
|
||||||
"schema",
|
"period",
|
||||||
"schemabinding",
|
"persisted",
|
||||||
"scoped",
|
"pi",
|
||||||
"scroll",
|
"plan",
|
||||||
"secondary",
|
"policy",
|
||||||
"security",
|
"power",
|
||||||
"select",
|
"precision",
|
||||||
"send",
|
"predicate",
|
||||||
"sent",
|
"primary",
|
||||||
"sequence",
|
"print",
|
||||||
"server",
|
"prior",
|
||||||
"session",
|
"proc",
|
||||||
"set",
|
"procedure",
|
||||||
"sets",
|
"public",
|
||||||
"setuser",
|
"query_store",
|
||||||
"simple",
|
"quoted_identifier",
|
||||||
"smallint",
|
"quotename",
|
||||||
"smallmoney",
|
"radians",
|
||||||
"snapshot",
|
"raiserror",
|
||||||
"sql",
|
"rand",
|
||||||
"standard",
|
"range",
|
||||||
"start",
|
"raw",
|
||||||
"started",
|
"read",
|
||||||
"state",
|
"read_committed_snapshot",
|
||||||
"statement",
|
"read_only",
|
||||||
"static",
|
"read_write",
|
||||||
"statistics",
|
"readonly",
|
||||||
"statistics_norecompute",
|
"readtext",
|
||||||
"status",
|
"real",
|
||||||
"stopped",
|
"rebuild",
|
||||||
"sysname",
|
"receive",
|
||||||
"system",
|
"reconfigure",
|
||||||
"system_time",
|
"recovery",
|
||||||
"table",
|
"recursive",
|
||||||
"take",
|
"recursive_triggers",
|
||||||
"target",
|
"references",
|
||||||
"then",
|
"relative",
|
||||||
"throw",
|
"remove",
|
||||||
"time",
|
"reorganize",
|
||||||
"timestamp",
|
"replace",
|
||||||
"tinyint",
|
"replicate",
|
||||||
"to",
|
"required",
|
||||||
"top",
|
"restart",
|
||||||
"tran",
|
"restore",
|
||||||
"transaction",
|
"restrict",
|
||||||
"trigger",
|
"resume",
|
||||||
"truncate",
|
"return",
|
||||||
"try",
|
"returns",
|
||||||
"tsql",
|
"reverse",
|
||||||
"type",
|
"revert",
|
||||||
"uncommitted",
|
"revoke",
|
||||||
"union",
|
"right",
|
||||||
"unique",
|
"rollback",
|
||||||
"uniqueidentifier",
|
"rollup",
|
||||||
"update",
|
"round",
|
||||||
"updatetext",
|
"row",
|
||||||
"use",
|
"rowcount",
|
||||||
"user",
|
"rowguidcol",
|
||||||
"using",
|
"rows",
|
||||||
"value",
|
"rtrim",
|
||||||
"values",
|
"rule",
|
||||||
"varchar",
|
"sample",
|
||||||
"version",
|
"save",
|
||||||
"view",
|
"schema",
|
||||||
"waitfor",
|
"schemabinding",
|
||||||
"when",
|
"scoped",
|
||||||
"where",
|
"scroll",
|
||||||
"while",
|
"secondary",
|
||||||
"with",
|
"security",
|
||||||
"within",
|
"select",
|
||||||
"without",
|
"send",
|
||||||
"writetext",
|
"sent",
|
||||||
"xact_abort",
|
"sequence",
|
||||||
"xml",
|
"server",
|
||||||
|
"session",
|
||||||
|
"session_user",
|
||||||
|
"sessionproperty",
|
||||||
|
"set",
|
||||||
|
"sets",
|
||||||
|
"setuser",
|
||||||
|
"sign",
|
||||||
|
"simple",
|
||||||
|
"sin",
|
||||||
|
"smallint",
|
||||||
|
"smallmoney",
|
||||||
|
"snapshot",
|
||||||
|
"soundex",
|
||||||
|
"space",
|
||||||
|
"sql",
|
||||||
|
"sqrt",
|
||||||
|
"square",
|
||||||
|
"standard",
|
||||||
|
"start",
|
||||||
|
"started",
|
||||||
|
"state",
|
||||||
|
"statement",
|
||||||
|
"static",
|
||||||
|
"statistics",
|
||||||
|
"statistics_norecompute",
|
||||||
|
"status",
|
||||||
|
"stdev",
|
||||||
|
"stdevp",
|
||||||
|
"stopped",
|
||||||
|
"str",
|
||||||
|
"string_agg",
|
||||||
|
"stuff",
|
||||||
|
"substring",
|
||||||
|
"sum",
|
||||||
|
"sysdatetime",
|
||||||
|
"sysname",
|
||||||
|
"system",
|
||||||
|
"system_time",
|
||||||
|
"system_user",
|
||||||
|
"table",
|
||||||
|
"take",
|
||||||
|
"tan",
|
||||||
|
"target",
|
||||||
|
"then",
|
||||||
|
"throw",
|
||||||
|
"time",
|
||||||
|
"timestamp",
|
||||||
|
"tinyint",
|
||||||
|
"to",
|
||||||
|
"top",
|
||||||
|
"tran",
|
||||||
|
"transaction",
|
||||||
|
"translate",
|
||||||
|
"trigger",
|
||||||
|
"trim",
|
||||||
|
"truncate",
|
||||||
|
"try",
|
||||||
|
"tsql",
|
||||||
|
"type",
|
||||||
|
"uncommitted",
|
||||||
|
"unicode",
|
||||||
|
"union",
|
||||||
|
"unique",
|
||||||
|
"uniqueidentifier",
|
||||||
|
"update",
|
||||||
|
"updatetext",
|
||||||
|
"upper",
|
||||||
|
"use",
|
||||||
|
"user",
|
||||||
|
"user_name",
|
||||||
|
"using",
|
||||||
|
"value",
|
||||||
|
"values",
|
||||||
|
"var",
|
||||||
|
"varchar",
|
||||||
|
"varp",
|
||||||
|
"version",
|
||||||
|
"view",
|
||||||
|
"waitfor",
|
||||||
|
"when",
|
||||||
|
"where",
|
||||||
|
"while",
|
||||||
|
"with",
|
||||||
|
"within",
|
||||||
|
"without",
|
||||||
|
"writetext",
|
||||||
|
"xact_abort",
|
||||||
|
"xml",
|
||||||
|
"year",
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -367,7 +456,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
int startColumn = scriptDocumentInfo.StartColumn;
|
int startColumn = scriptDocumentInfo.StartColumn;
|
||||||
int endColumn = scriptDocumentInfo.EndColumn;
|
int endColumn = scriptDocumentInfo.EndColumn;
|
||||||
string tokenText = scriptDocumentInfo.TokenText;
|
string tokenText = scriptDocumentInfo.TokenText;
|
||||||
// determine how many default completion items there will be
|
// determine how many default completion items there will be
|
||||||
int listSize = DefaultCompletionText.Length;
|
int listSize = DefaultCompletionText.Length;
|
||||||
if (!string.IsNullOrWhiteSpace(tokenText))
|
if (!string.IsNullOrWhiteSpace(tokenText))
|
||||||
{
|
{
|
||||||
@@ -392,14 +481,14 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
int completionItemIndex = 0;
|
int completionItemIndex = 0;
|
||||||
foreach (var completionText in DefaultCompletionText)
|
foreach (var completionText in DefaultCompletionText)
|
||||||
{
|
{
|
||||||
// add item to list if the tokenText is null (meaning return whole list)
|
// add item to list if the tokenText is null (meaning return whole list)
|
||||||
// or if the completion item begins with the tokenText
|
// or if the completion item begins with the tokenText
|
||||||
if (string.IsNullOrWhiteSpace(tokenText) || completionText.StartsWith(tokenText, StringComparison.OrdinalIgnoreCase))
|
if (string.IsNullOrWhiteSpace(tokenText) || completionText.StartsWith(tokenText, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
completionItems[completionItemIndex] = CreateDefaultCompletionItem(
|
completionItems[completionItemIndex] = CreateDefaultCompletionItem(
|
||||||
useLowerCase ? completionText.ToLowerInvariant() : completionText.ToUpperInvariant(),
|
useLowerCase ? completionText.ToLowerInvariant() : completionText.ToUpperInvariant(),
|
||||||
row,
|
row,
|
||||||
startColumn,
|
startColumn,
|
||||||
endColumn);
|
endColumn);
|
||||||
++completionItemIndex;
|
++completionItemIndex;
|
||||||
}
|
}
|
||||||
@@ -417,8 +506,8 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
/// <param name="endColumn"></param>
|
/// <param name="endColumn"></param>
|
||||||
private static CompletionItem CreateDefaultCompletionItem(
|
private static CompletionItem CreateDefaultCompletionItem(
|
||||||
string label,
|
string label,
|
||||||
int row,
|
int row,
|
||||||
int startColumn,
|
int startColumn,
|
||||||
int endColumn)
|
int endColumn)
|
||||||
{
|
{
|
||||||
return SqlCompletionItem.CreateCompletionItem(label, label + " keyword", label, CompletionItemKind.Keyword, row, startColumn, endColumn);
|
return SqlCompletionItem.CreateCompletionItem(label, label + " keyword", label, CompletionItemKind.Keyword, row, startColumn, endColumn);
|
||||||
@@ -450,14 +539,14 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
/// <param name="cursorColumn"></param>
|
/// <param name="cursorColumn"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
internal static CompletionItem[] ConvertDeclarationsToCompletionItems(
|
internal static CompletionItem[] ConvertDeclarationsToCompletionItems(
|
||||||
IEnumerable<Declaration> suggestions,
|
IEnumerable<Declaration> suggestions,
|
||||||
int row,
|
int row,
|
||||||
int startColumn,
|
int startColumn,
|
||||||
int endColumn,
|
int endColumn,
|
||||||
string tokenText = null)
|
string tokenText = null)
|
||||||
{
|
{
|
||||||
List<CompletionItem> completions = new List<CompletionItem>();
|
List<CompletionItem> completions = new List<CompletionItem>();
|
||||||
|
|
||||||
foreach (var autoCompleteItem in suggestions)
|
foreach (var autoCompleteItem in suggestions)
|
||||||
{
|
{
|
||||||
SqlCompletionItem sqlCompletionItem = new SqlCompletionItem(autoCompleteItem, tokenText);
|
SqlCompletionItem sqlCompletionItem = new SqlCompletionItem(autoCompleteItem, tokenText);
|
||||||
@@ -489,7 +578,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
markedStrings[0] = new MarkedString()
|
markedStrings[0] = new MarkedString()
|
||||||
{
|
{
|
||||||
Language = "SQL",
|
Language = "SQL",
|
||||||
Value = quickInfo.Text
|
Value = quickInfo.Text
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Hover()
|
return new Hover()
|
||||||
@@ -535,7 +624,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
// Signature label format: <name> param1, param2, ..., paramn RETURNS <type>
|
// Signature label format: <name> param1, param2, ..., paramn RETURNS <type>
|
||||||
Label = method.Name + " " + method.Parameters.Select(parameter => parameter.Display).Aggregate((l, r) => l + "," + r) + " " + method.Type,
|
Label = method.Name + " " + method.Parameters.Select(parameter => parameter.Display).Aggregate((l, r) => l + "," + r) + " " + method.Type,
|
||||||
Documentation = method.Description,
|
Documentation = method.Description,
|
||||||
Parameters = method.Parameters.Select(parameter =>
|
Parameters = method.Parameters.Select(parameter =>
|
||||||
{
|
{
|
||||||
return new ParameterInformation()
|
return new ParameterInformation()
|
||||||
{
|
{
|
||||||
@@ -560,7 +649,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
if (locations.ParamStartLocation != null)
|
if (locations.ParamStartLocation != null)
|
||||||
{
|
{
|
||||||
// Is the cursor past the function name?
|
// Is the cursor past the function name?
|
||||||
var location = locations.ParamStartLocation.Value;
|
var location = locations.ParamStartLocation.Value;
|
||||||
if (line > location.LineNumber || (line == location.LineNumber && line == location.LineNumber && column >= location.ColumnNumber))
|
if (line > location.LineNumber || (line == location.LineNumber && line == location.LineNumber && column >= location.ColumnNumber))
|
||||||
{
|
{
|
||||||
currentParameter = 0;
|
currentParameter = 0;
|
||||||
@@ -577,7 +666,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
if (locations.ParamEndLocation != null)
|
if (locations.ParamEndLocation != null)
|
||||||
{
|
{
|
||||||
// Is the cursor past the end of the parameter list on a different token?
|
// Is the cursor past the end of the parameter list on a different token?
|
||||||
var location = locations.ParamEndLocation.Value;
|
var location = locations.ParamEndLocation.Value;
|
||||||
if (line > location.LineNumber || (line == location.LineNumber && line == location.LineNumber && column > location.ColumnNumber))
|
if (line > location.LineNumber || (line == location.LineNumber && line == location.LineNumber && column > location.ColumnNumber))
|
||||||
{
|
{
|
||||||
currentParameter = -1;
|
currentParameter = -1;
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// 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.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion.Extension
|
||||||
|
{
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class CompletionExtensionParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Absolute path for the assembly containing the completion extension
|
||||||
|
/// </summary>
|
||||||
|
public string AssemblyPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type name for the completion extension
|
||||||
|
/// </summary>
|
||||||
|
public string TypeName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Property bag for initializing the completion extension
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, object> Properties { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// 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.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion.Extension
|
||||||
|
{
|
||||||
|
public interface ICompletionExtension : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unique name for the extension
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method for initializing the extension, this is called once when the extension is loaded
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="properties">Parameters needed by the extension</param>
|
||||||
|
/// <param name="cancelToken">Cancellation token used to indicate that the initialization should be cancelled</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task Initialize(IReadOnlyDictionary<string, object> properties, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the completion request, returning the modified CompletionItemList if used
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connInfo">Connection information for the completion session</param>
|
||||||
|
/// <param name="scriptDocumentInfo">Script parsing information</param>
|
||||||
|
/// <param name="completions">Current completion list</param>
|
||||||
|
/// <param name="cancelToken">Token used to indicate that the completion request should be cancelled</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<CompletionItem[]> HandleCompletionAsync(ConnectionInfo connInfo, ScriptDocumentInfo scriptDocumentInfo, CompletionItem[] completions, CancellationToken cancelToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion.Extension;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
@@ -23,6 +24,13 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
|||||||
RequestType<CompletionItem, CompletionItem>.Create("completionItem/resolve");
|
RequestType<CompletionItem, CompletionItem>.Create("completionItem/resolve");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CompletionExtLoadRequest
|
||||||
|
{
|
||||||
|
public static readonly
|
||||||
|
RequestType<CompletionExtensionParams, bool> Type =
|
||||||
|
RequestType<CompletionExtensionParams, bool>.Create("completion/extLoad");
|
||||||
|
}
|
||||||
|
|
||||||
public enum CompletionItemKind
|
public enum CompletionItemKind
|
||||||
{
|
{
|
||||||
Text = 1,
|
Text = 1,
|
||||||
@@ -45,6 +53,19 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
|||||||
Reference = 18
|
Reference = 18
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class Command
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The identifier of the actual command handler, like `vsintellicode.completionItemSelected`.
|
||||||
|
/// </summary>
|
||||||
|
public string CommandStr { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments that the command handler should be invoked with.
|
||||||
|
/// </summary>
|
||||||
|
public object[] Arguments { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
[DebuggerDisplay("Kind = {Kind.ToString()}, Label = {Label}, Detail = {Detail}")]
|
[DebuggerDisplay("Kind = {Kind.ToString()}, Label = {Label}, Detail = {Detail}")]
|
||||||
public class CompletionItem
|
public class CompletionItem
|
||||||
{
|
{
|
||||||
@@ -74,5 +95,15 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
|||||||
/// resolve request.
|
/// resolve request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object Data { get; set; }
|
public object Data { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposing a command field for a completion item for passing telemetry
|
||||||
|
/// </summary>
|
||||||
|
public Command Command { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this completion item is preselected or not
|
||||||
|
/// </summary>
|
||||||
|
public bool? Preselect { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ using System;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Loader;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlServer.Management.Common;
|
using Microsoft.SqlServer.Management.Common;
|
||||||
@@ -17,13 +20,14 @@ using Microsoft.SqlServer.Management.SqlParser.Common;
|
|||||||
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
|
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
|
||||||
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
||||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||||
|
using Microsoft.SqlTools.Extensibility;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol;
|
using Microsoft.SqlTools.Hosting.Protocol;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion;
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion.Extension;
|
||||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
|
||||||
using Microsoft.SqlTools.ServiceLayer.Scripting;
|
using Microsoft.SqlTools.ServiceLayer.Scripting;
|
||||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||||
@@ -72,6 +76,10 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
|
|
||||||
internal const int PeekDefinitionTimeout = 10 * OneSecond;
|
internal const int PeekDefinitionTimeout = 10 * OneSecond;
|
||||||
|
|
||||||
|
internal const int ExtensionLoadingTimeout = 10 * OneSecond;
|
||||||
|
|
||||||
|
internal const int CompletionExtTimeout = 200;
|
||||||
|
|
||||||
private ConnectionService connectionService = null;
|
private ConnectionService connectionService = null;
|
||||||
|
|
||||||
private WorkspaceService<SqlToolsSettings> workspaceServiceInstance;
|
private WorkspaceService<SqlToolsSettings> workspaceServiceInstance;
|
||||||
@@ -95,6 +103,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
private Lazy<Dictionary<string, ScriptParseInfo>> scriptParseInfoMap
|
private Lazy<Dictionary<string, ScriptParseInfo>> scriptParseInfoMap
|
||||||
= new Lazy<Dictionary<string, ScriptParseInfo>>(() => new Dictionary<string, ScriptParseInfo>());
|
= new Lazy<Dictionary<string, ScriptParseInfo>>(() => new Dictionary<string, ScriptParseInfo>());
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, ICompletionExtension> completionExtensions = new ConcurrentDictionary<string, ICompletionExtension>();
|
||||||
|
private readonly ConcurrentDictionary<string, DateTime> extAssemblyLastUpdateTime = new ConcurrentDictionary<string, DateTime>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a mapping dictionary for SQL file URIs to ScriptParseInfo objects
|
/// Gets a mapping dictionary for SQL file URIs to ScriptParseInfo objects
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -245,6 +256,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
|
serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
|
||||||
serviceHost.SetRequestHandler(DefinitionRequest.Type, HandleDefinitionRequest);
|
serviceHost.SetRequestHandler(DefinitionRequest.Type, HandleDefinitionRequest);
|
||||||
serviceHost.SetRequestHandler(SyntaxParseRequest.Type, HandleSyntaxParseRequest);
|
serviceHost.SetRequestHandler(SyntaxParseRequest.Type, HandleSyntaxParseRequest);
|
||||||
|
serviceHost.SetRequestHandler(CompletionExtLoadRequest.Type, HandleCompletionExtLoadRequest);
|
||||||
serviceHost.SetEventHandler(RebuildIntelliSenseNotification.Type, HandleRebuildIntelliSenseNotification);
|
serviceHost.SetEventHandler(RebuildIntelliSenseNotification.Type, HandleRebuildIntelliSenseNotification);
|
||||||
serviceHost.SetEventHandler(LanguageFlavorChangeNotification.Type, HandleDidChangeLanguageFlavorNotification);
|
serviceHost.SetEventHandler(LanguageFlavorChangeNotification.Type, HandleDidChangeLanguageFlavorNotification);
|
||||||
|
|
||||||
@@ -283,6 +295,90 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
|
|
||||||
#region Request Handlers
|
#region Request Handlers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Completion extension load request callback
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="requestContext"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal async Task HandleCompletionExtLoadRequest(CompletionExtensionParams param, RequestContext<bool> requestContext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//register the new assembly
|
||||||
|
var serviceProvider = (ExtensionServiceProvider)ServiceHostInstance.ServiceProvider;
|
||||||
|
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(param.AssemblyPath);
|
||||||
|
var assemblies = new Assembly[] { assembly };
|
||||||
|
serviceProvider.AddAssembliesToConfiguration(assemblies);
|
||||||
|
foreach (var ext in serviceProvider.GetServices<ICompletionExtension>())
|
||||||
|
{
|
||||||
|
var cancellationTokenSource = new CancellationTokenSource(ExtensionLoadingTimeout);
|
||||||
|
var cancellationToken = cancellationTokenSource.Token;
|
||||||
|
string extName = ext.Name;
|
||||||
|
string extTypeName = ext.GetType().FullName;
|
||||||
|
if (extTypeName != param.TypeName)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CheckIfAssemblyShouldBeLoaded(param.AssemblyPath, extTypeName))
|
||||||
|
{
|
||||||
|
await requestContext.SendError(string.Format("Skip loading {0} because it's already loaded", param.AssemblyPath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ext.Initialize(param.Properties, cancellationToken).WithTimeout(ExtensionLoadingTimeout);
|
||||||
|
cancellationTokenSource.Dispose();
|
||||||
|
if (!string.IsNullOrEmpty(extName))
|
||||||
|
{
|
||||||
|
completionExtensions[extName] = ext;
|
||||||
|
await requestContext.SendResult(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await requestContext.SendError(string.Format("Skip loading an unnamed completion extension from {0}", param.AssemblyPath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await requestContext.SendError(ex.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await requestContext.SendError(string.Format("Couldn't discover completion extension with type {0} in {1}", param.TypeName, param.AssemblyPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check whether a particular assembly should be reloaded based on
|
||||||
|
/// whether it's been updated since it was last loaded.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assemblyPath">The assembly path</param>
|
||||||
|
/// <param name="extTypeName">The type loading from the assembly</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private bool CheckIfAssemblyShouldBeLoaded(string assemblyPath, string extTypeName)
|
||||||
|
{
|
||||||
|
var lastModified = File.GetLastWriteTime(assemblyPath);
|
||||||
|
if (extAssemblyLastUpdateTime.ContainsKey(extTypeName))
|
||||||
|
{
|
||||||
|
if (lastModified > extAssemblyLastUpdateTime[extTypeName])
|
||||||
|
{
|
||||||
|
extAssemblyLastUpdateTime[extTypeName] = lastModified;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
extAssemblyLastUpdateTime[extTypeName] = lastModified;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// T-SQL syntax parse request callback
|
/// T-SQL syntax parse request callback
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -352,7 +448,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
scriptFile.ClientFilePath,
|
scriptFile.ClientFilePath,
|
||||||
out connInfo);
|
out connInfo);
|
||||||
|
|
||||||
var completionItems = GetCompletionItems(
|
var completionItems = await GetCompletionItems(
|
||||||
textDocumentPosition, scriptFile, connInfo);
|
textDocumentPosition, scriptFile, connInfo);
|
||||||
|
|
||||||
await requestContext.SendResult(completionItems);
|
await requestContext.SendResult(completionItems);
|
||||||
@@ -1466,7 +1562,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
/// This method does not await cache builds since it expects to return quickly
|
/// This method does not await cache builds since it expects to return quickly
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="textDocumentPosition"></param>
|
/// <param name="textDocumentPosition"></param>
|
||||||
public CompletionItem[] GetCompletionItems(
|
public async Task<CompletionItem[]> GetCompletionItems(
|
||||||
TextDocumentPosition textDocumentPosition,
|
TextDocumentPosition textDocumentPosition,
|
||||||
ScriptFile scriptFile,
|
ScriptFile scriptFile,
|
||||||
ConnectionInfo connInfo)
|
ConnectionInfo connInfo)
|
||||||
@@ -1482,7 +1578,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
|
|
||||||
if (scriptParseInfo == null)
|
if (scriptParseInfo == null)
|
||||||
{
|
{
|
||||||
return AutoCompleteHelper.GetDefaultCompletionItems(ScriptDocumentInfo.CreateDefaultDocumentInfo(textDocumentPosition, scriptFile), useLowerCaseSuggestions);
|
var scriptDocInfo = ScriptDocumentInfo.CreateDefaultDocumentInfo(textDocumentPosition, scriptFile);
|
||||||
|
resultCompletionItems = AutoCompleteHelper.GetDefaultCompletionItems(scriptDocInfo, useLowerCaseSuggestions);
|
||||||
|
//call completion extensions only for default completion list
|
||||||
|
resultCompletionItems = await ApplyCompletionExtensions(connInfo, resultCompletionItems, scriptDocInfo);
|
||||||
|
return resultCompletionItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptDocumentInfo scriptDocumentInfo = new ScriptDocumentInfo(textDocumentPosition, scriptFile, scriptParseInfo);
|
ScriptDocumentInfo scriptDocumentInfo = new ScriptDocumentInfo(textDocumentPosition, scriptFile, scriptParseInfo);
|
||||||
@@ -1496,7 +1596,10 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
// if the parse failed then return the default list
|
// if the parse failed then return the default list
|
||||||
if (scriptParseInfo.ParseResult == null)
|
if (scriptParseInfo.ParseResult == null)
|
||||||
{
|
{
|
||||||
return AutoCompleteHelper.GetDefaultCompletionItems(scriptDocumentInfo, useLowerCaseSuggestions);
|
resultCompletionItems = AutoCompleteHelper.GetDefaultCompletionItems(scriptDocumentInfo, useLowerCaseSuggestions);
|
||||||
|
//call completion extensions only for default completion list
|
||||||
|
resultCompletionItems = await ApplyCompletionExtensions(connInfo, resultCompletionItems, scriptDocumentInfo);
|
||||||
|
return resultCompletionItems;
|
||||||
}
|
}
|
||||||
AutoCompletionResult result = completionService.CreateCompletions(connInfo, scriptDocumentInfo, useLowerCaseSuggestions);
|
AutoCompletionResult result = completionService.CreateCompletions(connInfo, scriptDocumentInfo, useLowerCaseSuggestions);
|
||||||
// cache the current script parse info object to resolve completions later
|
// cache the current script parse info object to resolve completions later
|
||||||
@@ -1507,6 +1610,38 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
if (resultCompletionItems == null)
|
if (resultCompletionItems == null)
|
||||||
{
|
{
|
||||||
resultCompletionItems = AutoCompleteHelper.GetDefaultCompletionItems(scriptDocumentInfo, useLowerCaseSuggestions);
|
resultCompletionItems = AutoCompleteHelper.GetDefaultCompletionItems(scriptDocumentInfo, useLowerCaseSuggestions);
|
||||||
|
//call completion extensions only for default completion list
|
||||||
|
resultCompletionItems = await ApplyCompletionExtensions(connInfo, resultCompletionItems, scriptDocumentInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultCompletionItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Run all completion extensions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connInfo"></param>
|
||||||
|
/// <param name="resultCompletionItems"></param>
|
||||||
|
/// <param name="scriptDocumentInfo"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task<CompletionItem[]> ApplyCompletionExtensions(ConnectionInfo connInfo, CompletionItem[] resultCompletionItems, ScriptDocumentInfo scriptDocumentInfo)
|
||||||
|
{
|
||||||
|
//invoke the completion extensions
|
||||||
|
foreach (var completionExt in completionExtensions.Values)
|
||||||
|
{
|
||||||
|
var cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
cancellationTokenSource.CancelAfter(CompletionExtTimeout);
|
||||||
|
var cancellationToken = cancellationTokenSource.Token;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
resultCompletionItems = await completionExt.HandleCompletionAsync(connInfo, scriptDocumentInfo, resultCompletionItems, cancellationToken).WithTimeout(CompletionExtTimeout);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Write(TraceEventType.Error, string.Format("Exception in calling completion extension {0}:\n{1}", completionExt.Name, e.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationTokenSource.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultCompletionItems;
|
return resultCompletionItems;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A class to calculate the numbers used by SQL parser using the text positions and content
|
/// A class to calculate the numbers used by SQL parser using the text positions and content
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class ScriptDocumentInfo
|
public class ScriptDocumentInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create new instance
|
/// Create new instance
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class for storing cached metadata regarding a parsed SQL file
|
/// Class for storing cached metadata regarding a parsed SQL file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class ScriptParseInfo
|
public class ScriptParseInfo
|
||||||
{
|
{
|
||||||
private object buildingMetadataLock = new object();
|
private object buildingMetadataLock = new object();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//
|
//
|
||||||
// Copyright (c) Microsoft. All rights reserved.
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlTools.Utility;
|
using Microsoft.SqlTools.Utility;
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
/// Adds handling to check the Exception field of a task and log it if the task faulted
|
/// Adds handling to check the Exception field of a task and log it if the task faulted
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This will effectively swallow exceptions in the task chain.
|
/// This will effectively swallow exceptions in the task chain.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="antecedent">The task to continue</param>
|
/// <param name="antecedent">The task to continue</param>
|
||||||
/// <param name="continuationAction">
|
/// <param name="continuationAction">
|
||||||
@@ -33,7 +34,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogTaskExceptions(task.Exception);
|
LogTaskExceptions(task.Exception);
|
||||||
|
|
||||||
// Run the continuation task that was provided
|
// Run the continuation task that was provided
|
||||||
@@ -54,7 +55,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
/// This version allows for async code to be ran in the continuation function.
|
/// This version allows for async code to be ran in the continuation function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This will effectively swallow exceptions in the task chain.
|
/// This will effectively swallow exceptions in the task chain.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="antecedent">The task to continue</param>
|
/// <param name="antecedent">The task to continue</param>
|
||||||
/// <param name="continuationFunc">
|
/// <param name="continuationFunc">
|
||||||
@@ -97,5 +98,38 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
}
|
}
|
||||||
Logger.Write(TraceEventType.Error, sb.ToString());
|
Logger.Write(TraceEventType.Error, sb.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This will enforce time out to run an async task with returning result
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TResult"></typeparam>
|
||||||
|
/// <param name="task">The async task to run</param>
|
||||||
|
/// <param name="timeout">Time out in milliseconds</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<TResult> WithTimeout<TResult>(this Task<TResult> task, int timeout)
|
||||||
|
{
|
||||||
|
if (task == await Task.WhenAny(task, Task.Delay(timeout)))
|
||||||
|
{
|
||||||
|
return await task;
|
||||||
|
}
|
||||||
|
throw new TimeoutException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This will enforce time out to run an async task without returning result
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TResult"></typeparam>
|
||||||
|
/// <param name="task">The async task to run</param>
|
||||||
|
/// <param name="timeout">Time out in milliseconds</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task WithTimeout(this Task task, int timeout)
|
||||||
|
{
|
||||||
|
if (task == await Task.WhenAny(task, Task.Delay(timeout)))
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new TimeoutException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
81
test/CompletionExtSample/CompletionExt.cs
Normal file
81
test/CompletionExtSample/CompletionExt.cs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
//
|
||||||
|
// 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.ComponentModel.Composition;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion.Extension;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.Test.CompletionExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
[Export(typeof(ICompletionExtension))]
|
||||||
|
public class CompletionExt : ICompletionExtension
|
||||||
|
{
|
||||||
|
public string Name => "CompletionExt";
|
||||||
|
|
||||||
|
private string modelPath;
|
||||||
|
|
||||||
|
public CompletionExt()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDisposable.Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<CompletionItem[]> ICompletionExtension.HandleCompletionAsync(ConnectionInfo connInfo, ScriptDocumentInfo scriptDocumentInfo, CompletionItem[] completions, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (completions == null || completions == null || completions.Length == 0)
|
||||||
|
{
|
||||||
|
return completions;
|
||||||
|
}
|
||||||
|
return await Run(completions, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task ICompletionExtension.Initialize(IReadOnlyDictionary<string, object> properties, CancellationToken token)
|
||||||
|
{
|
||||||
|
modelPath = (string)properties["modelPath"];
|
||||||
|
await LoadModel(token).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadModel(CancellationToken token)
|
||||||
|
{
|
||||||
|
//loading model logic here
|
||||||
|
await Task.Delay(2000).ConfigureAwait(false); //for testing
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
Console.WriteLine("Model loaded from: " + modelPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<CompletionItem[]> Run(CompletionItem[] completions, CancellationToken token)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Enter ExecuteAsync");
|
||||||
|
|
||||||
|
var sortedItems = completions.OrderBy(item => item.SortText);
|
||||||
|
sortedItems.First().Preselect = true;
|
||||||
|
foreach(var item in sortedItems)
|
||||||
|
{
|
||||||
|
item.Command = new Command
|
||||||
|
{
|
||||||
|
CommandStr = "vsintellicode.completionItemSelected",
|
||||||
|
Arguments = new object[] { new Dictionary<string, string> { { "IsCommit", "True" } } }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//code to augment the default completion list
|
||||||
|
await Task.Delay(20); // for testing
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
Console.WriteLine("Exit ExecuteAsync");
|
||||||
|
return sortedItems.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../../Common.props" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -3,14 +3,21 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
|
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
|
||||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion.Extension;
|
||||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.UnitTests.ServiceHost;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
|
||||||
@@ -75,9 +82,9 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test currently requires a live database connection to initialize
|
/// <summary>
|
||||||
// SMO connected metadata provider. Since we don't want a live DB dependency
|
/// This test tests auto completion
|
||||||
// in the CI unit tests this scenario is currently disabled.
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AutoCompleteFindCompletions()
|
public void AutoCompleteFindCompletions()
|
||||||
{
|
{
|
||||||
@@ -90,11 +97,116 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
|
|||||||
var completions = autoCompleteService.GetCompletionItems(
|
var completions = autoCompleteService.GetCompletionItems(
|
||||||
result.TextDocumentPosition,
|
result.TextDocumentPosition,
|
||||||
result.ScriptFile,
|
result.ScriptFile,
|
||||||
result.ConnectionInfo);
|
result.ConnectionInfo).Result;
|
||||||
|
|
||||||
Assert.True(completions.Length > 0);
|
Assert.True(completions.Length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string AssemblyDirectory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
|
||||||
|
UriBuilder uri = new UriBuilder(codeBase);
|
||||||
|
string path = Uri.UnescapeDataString(uri.Path);
|
||||||
|
return Path.GetDirectoryName(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This test tests completion extension interface in following aspects
|
||||||
|
/// 1. Loading a sample completion extension assembly
|
||||||
|
/// 2. Initializing a completion extension implementation
|
||||||
|
/// 3. Excuting an auto completion with extension enabled
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async void AutoCompleteWithExtension()
|
||||||
|
{
|
||||||
|
var result = GetLiveAutoCompleteTestObjects();
|
||||||
|
|
||||||
|
result.TextDocumentPosition.Position.Character = 10;
|
||||||
|
result.ScriptFile = ScriptFileTests.GetTestScriptFile("select * f");
|
||||||
|
result.TextDocumentPosition.TextDocument.Uri = result.ScriptFile.FilePath;
|
||||||
|
|
||||||
|
var autoCompleteService = LanguageService.Instance;
|
||||||
|
var requestContext = new Mock<SqlTools.Hosting.Protocol.RequestContext<bool>>();
|
||||||
|
requestContext.Setup(x => x.SendResult(It.IsAny<bool>()))
|
||||||
|
.Returns(Task.FromResult(true));
|
||||||
|
requestContext.Setup(x => x.SendError(It.IsAny<string>(), 0))
|
||||||
|
.Returns(Task.FromResult(true));
|
||||||
|
|
||||||
|
//Create completion extension parameters
|
||||||
|
var extensionParams = new CompletionExtensionParams()
|
||||||
|
{
|
||||||
|
AssemblyPath = Path.Combine(AssemblyDirectory, "Microsoft.SqlTools.Test.CompletionExtension.dll"),
|
||||||
|
TypeName = "Microsoft.SqlTools.Test.CompletionExtension.CompletionExt",
|
||||||
|
Properties = new Dictionary<string, object> { { "modelPath", "testModel" } }
|
||||||
|
};
|
||||||
|
|
||||||
|
//load and initialize completion extension, expect a success
|
||||||
|
await autoCompleteService.HandleCompletionExtLoadRequest(extensionParams, requestContext.Object);
|
||||||
|
|
||||||
|
requestContext.Verify(x => x.SendResult(It.IsAny<bool>()), Times.Once);
|
||||||
|
requestContext.Verify(x => x.SendError(It.IsAny<string>(), 0), Times.Never);
|
||||||
|
|
||||||
|
//Try to load the same completion extension second time, expect an error sent
|
||||||
|
await autoCompleteService.HandleCompletionExtLoadRequest(extensionParams, requestContext.Object);
|
||||||
|
|
||||||
|
requestContext.Verify(x => x.SendResult(It.IsAny<bool>()), Times.Once);
|
||||||
|
requestContext.Verify(x => x.SendError(It.IsAny<string>(), 0), Times.Once);
|
||||||
|
|
||||||
|
//Try to load the completion extension with new modified timestamp, expect a success
|
||||||
|
var assemblyCopyPath = CopyFileWithNewModifiedTime(extensionParams.AssemblyPath);
|
||||||
|
extensionParams = new CompletionExtensionParams()
|
||||||
|
{
|
||||||
|
AssemblyPath = assemblyCopyPath,
|
||||||
|
TypeName = "Microsoft.SqlTools.Test.CompletionExtension.CompletionExt",
|
||||||
|
Properties = new Dictionary<string, object> { { "modelPath", "testModel" } }
|
||||||
|
};
|
||||||
|
//load and initialize completion extension
|
||||||
|
await autoCompleteService.HandleCompletionExtLoadRequest(extensionParams, requestContext.Object);
|
||||||
|
|
||||||
|
requestContext.Verify(x => x.SendResult(It.IsAny<bool>()), Times.Exactly(2));
|
||||||
|
requestContext.Verify(x => x.SendError(It.IsAny<string>(), 0), Times.Once);
|
||||||
|
|
||||||
|
ScriptParseInfo scriptInfo = new ScriptParseInfo { IsConnected = true };
|
||||||
|
autoCompleteService.ParseAndBind(result.ScriptFile, result.ConnectionInfo);
|
||||||
|
scriptInfo.ConnectionKey = autoCompleteService.BindingQueue.AddConnectionContext(result.ConnectionInfo);
|
||||||
|
|
||||||
|
//Invoke auto completion with extension enabled
|
||||||
|
var completions = autoCompleteService.GetCompletionItems(
|
||||||
|
result.TextDocumentPosition,
|
||||||
|
result.ScriptFile,
|
||||||
|
result.ConnectionInfo).Result;
|
||||||
|
|
||||||
|
//Validate completion list is not empty
|
||||||
|
Assert.True(completions != null && completions.Length > 0, "The completion list is null or empty!");
|
||||||
|
//Validate the first completion item in the list is preselected
|
||||||
|
Assert.True(completions[0].Preselect.HasValue && completions[0].Preselect.Value, "Preselect is not set properly in the first completion item by the completion extension!");
|
||||||
|
//Validate the Command object attached to the completion item by the extension
|
||||||
|
Assert.True(completions[0].Command != null && completions[0].Command.CommandStr == "vsintellicode.completionItemSelected", "Command is not set properly in the first completion item by the completion extension!");
|
||||||
|
|
||||||
|
//clean up the temp file
|
||||||
|
File.Delete(assemblyCopyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make a copy of a file and update the last modified time
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private string CopyFileWithNewModifiedTime(string filePath)
|
||||||
|
{
|
||||||
|
var tempPath = Path.Combine(Path.GetTempPath(), Path.GetFileName(filePath));
|
||||||
|
if (File.Exists(tempPath))
|
||||||
|
{
|
||||||
|
File.Delete(tempPath);
|
||||||
|
}
|
||||||
|
File.Copy(filePath, tempPath);
|
||||||
|
File.SetLastWriteTimeUtc(tempPath, DateTime.UtcNow);
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verify that GetSignatureHelp returns not null when the provided TextDocumentPosition
|
/// Verify that GetSignatureHelp returns not null when the provided TextDocumentPosition
|
||||||
/// has an associated ScriptParseInfo and the provided query has a function that should
|
/// has an associated ScriptParseInfo and the provided query has a function that should
|
||||||
@@ -191,7 +303,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
|
|||||||
};
|
};
|
||||||
|
|
||||||
// First check that we don't have any items in the completion list as expected
|
// First check that we don't have any items in the completion list as expected
|
||||||
var initialCompletionItems = langService.GetCompletionItems(
|
var initialCompletionItems = await langService.GetCompletionItems(
|
||||||
textDocumentPosition, connectionInfoResult.ScriptFile, connectionInfoResult.ConnectionInfo);
|
textDocumentPosition, connectionInfoResult.ScriptFile, connectionInfoResult.ConnectionInfo);
|
||||||
|
|
||||||
Assert.True(initialCompletionItems.Length == 0, $"Should not have any completion items initially. Actual : [{string.Join(',', initialCompletionItems.Select(ci => ci.Label))}]");
|
Assert.True(initialCompletionItems.Length == 0, $"Should not have any completion items initially. Actual : [{string.Join(',', initialCompletionItems.Select(ci => ci.Label))}]");
|
||||||
@@ -205,7 +317,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
|
|||||||
new TestEventContext());
|
new TestEventContext());
|
||||||
|
|
||||||
// Now we should expect to see the item show up in the completion list
|
// Now we should expect to see the item show up in the completion list
|
||||||
var afterTableCreationCompletionItems = langService.GetCompletionItems(
|
var afterTableCreationCompletionItems = await langService.GetCompletionItems(
|
||||||
textDocumentPosition, connectionInfoResult.ScriptFile, connectionInfoResult.ConnectionInfo);
|
textDocumentPosition, connectionInfoResult.ScriptFile, connectionInfoResult.ConnectionInfo);
|
||||||
|
|
||||||
Assert.True(afterTableCreationCompletionItems.Length == 1, $"Should only have a single completion item after rebuilding Intellisense cache. Actual : [{string.Join(',', initialCompletionItems.Select(ci => ci.Label))}]");
|
Assert.True(afterTableCreationCompletionItems.Length == 1, $"Should only have a single completion item after rebuilding Intellisense cache. Actual : [{string.Join(',', initialCompletionItems.Select(ci => ci.Label))}]");
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
<ProjectReference Include="../Microsoft.SqlTools.ServiceLayer.Test.Common/Microsoft.SqlTools.ServiceLayer.Test.Common.csproj" />
|
<ProjectReference Include="../Microsoft.SqlTools.ServiceLayer.Test.Common/Microsoft.SqlTools.ServiceLayer.Test.Common.csproj" />
|
||||||
<ProjectReference Include="../../src/Microsoft.SqlTools.ManagedBatchParser/Microsoft.SqlTools.ManagedBatchParser.csproj" />
|
<ProjectReference Include="../../src/Microsoft.SqlTools.ManagedBatchParser/Microsoft.SqlTools.ManagedBatchParser.csproj" />
|
||||||
<ProjectReference Include="../Microsoft.SqlTools.ServiceLayer.UnitTests/Microsoft.SqlTools.ServiceLayer.UnitTests.csproj" />
|
<ProjectReference Include="../Microsoft.SqlTools.ServiceLayer.UnitTests/Microsoft.SqlTools.ServiceLayer.UnitTests.csproj" />
|
||||||
|
<ProjectReference Include="..\CompletionExtSample\Microsoft.SqlTools.Test.CompletionExtension.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Net.Http" Version="4.3.1" />
|
<PackageReference Include="System.Net.Http" Version="4.3.1" />
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
|
|||||||
{
|
{
|
||||||
InitializeTestObjects();
|
InitializeTestObjects();
|
||||||
textDocument.TextDocument.Uri = "somethinggoeshere";
|
textDocument.TextDocument.Uri = "somethinggoeshere";
|
||||||
Assert.True(langService.GetCompletionItems(textDocument, scriptFile.Object, null).Length > 0);
|
Assert.True(langService.GetCompletionItems(textDocument, scriptFile.Object, null).Result.Length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ServiceHost
|
|||||||
"SELECT * FROM sys.objects as o2" + Environment.NewLine +
|
"SELECT * FROM sys.objects as o2" + Environment.NewLine +
|
||||||
"SELECT * FROM sys.objects as o3" + Environment.NewLine;
|
"SELECT * FROM sys.objects as o3" + Environment.NewLine;
|
||||||
|
|
||||||
internal static ScriptFile GetTestScriptFile(string initialText = null)
|
public static ScriptFile GetTestScriptFile(string initialText = null)
|
||||||
{
|
{
|
||||||
if (initialText == null)
|
if (initialText == null)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user