mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Create MS.SqlTools.Credentials project (#249)
* Initial credential service files * Clean-up hostloader * Update build scripts to build credentials archive * Move hosting files to new assembly * Add credentials files to MS.SqlTools.Credentials * Remove duplicate files * Update namespace in program.cs * Fix test build breaks * Update extensions visibility. * Remove unused resource strings * Add xproj files to SLN for appveyor builds * Fix appveyor build break in test project * Fix extensibility tests * Fix various typos in latest iteration * Add settings for Integration build * Fix codecoverage.bat to use full pdb for new projects * Fix bug when packing in folder with native images * Fix typos in xproj * Reset XLF to fix build.cmd
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition.Convention;
|
||||
using System.Composition.Hosting;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Extensibility
|
||||
{
|
||||
public class ExtensionServiceProvider : RegisteredServiceProvider
|
||||
{
|
||||
private Func<ConventionBuilder, ContainerConfiguration> config;
|
||||
|
||||
public ExtensionServiceProvider(Func<ConventionBuilder, ContainerConfiguration> config)
|
||||
{
|
||||
Validate.IsNotNull(nameof(config), config);
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public static ExtensionServiceProvider CreateDefaultServiceProvider()
|
||||
{
|
||||
string assemblyPath = typeof(ExtensionStore).GetTypeInfo().Assembly.Location;
|
||||
string directory = Path.GetDirectoryName(assemblyPath);
|
||||
|
||||
AssemblyLoadContext context = new AssemblyLoader(directory);
|
||||
var assemblyPaths = Directory.GetFiles(directory, "*.dll", SearchOption.TopDirectoryOnly);
|
||||
|
||||
List<Assembly> assemblies = new List<Assembly>();
|
||||
foreach (var path in assemblyPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
assemblies.Add(
|
||||
context.LoadFromAssemblyName(
|
||||
AssemblyLoadContext.GetAssemblyName(path)));
|
||||
}
|
||||
catch (System.BadImageFormatException)
|
||||
{
|
||||
// we expect exceptions trying to scan all DLLs since directory contains native libraries
|
||||
}
|
||||
}
|
||||
|
||||
return Create(assemblies);
|
||||
}
|
||||
|
||||
public static ExtensionServiceProvider Create(IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
Validate.IsNotNull(nameof(assemblies), assemblies);
|
||||
return new ExtensionServiceProvider(conventions => new ContainerConfiguration().WithAssemblies(assemblies, conventions));
|
||||
}
|
||||
|
||||
public static ExtensionServiceProvider Create(IEnumerable<Type> types)
|
||||
{
|
||||
Validate.IsNotNull(nameof(types), types);
|
||||
return new ExtensionServiceProvider(conventions => new ContainerConfiguration().WithParts(types, conventions));
|
||||
}
|
||||
|
||||
protected override IEnumerable<T> GetServicesImpl<T>()
|
||||
{
|
||||
EnsureExtensionStoreRegistered<T>();
|
||||
return base.GetServicesImpl<T>();
|
||||
}
|
||||
|
||||
private void EnsureExtensionStoreRegistered<T>()
|
||||
{
|
||||
if (!services.ContainsKey(typeof(T)))
|
||||
{
|
||||
ExtensionStore store = new ExtensionStore(typeof(T), config);
|
||||
base.Register<T>(() => store.GetExports<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class ExtensionStore
|
||||
{
|
||||
private CompositionHost host;
|
||||
private IList exports;
|
||||
private Type contractType;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the store with a type to lookup exports of, and a function that configures the
|
||||
/// lookup parameters.
|
||||
/// </summary>
|
||||
/// <param name="contractType">Type to use as a base for all extensions being looked up</param>
|
||||
/// <param name="configure">Function that returns the configuration to be used</param>
|
||||
public ExtensionStore(Type contractType, Func<ConventionBuilder, ContainerConfiguration> configure)
|
||||
{
|
||||
Validate.IsNotNull(nameof(contractType), contractType);
|
||||
Validate.IsNotNull(nameof(configure), configure);
|
||||
this.contractType = contractType;
|
||||
ConventionBuilder builder = GetExportBuilder();
|
||||
ContainerConfiguration config = configure(builder);
|
||||
host = config.CreateContainer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads extensions from the current assembly
|
||||
/// </summary>
|
||||
/// <returns>ExtensionStore</returns>
|
||||
public static ExtensionStore CreateDefaultLoader<T>()
|
||||
{
|
||||
return CreateAssemblyStore<T>(typeof(ExtensionStore).GetTypeInfo().Assembly);
|
||||
}
|
||||
|
||||
public static ExtensionStore CreateAssemblyStore<T>(Assembly assembly)
|
||||
{
|
||||
Validate.IsNotNull(nameof(assembly), assembly);
|
||||
return new ExtensionStore(typeof(T), (conventions) =>
|
||||
new ContainerConfiguration().WithAssembly(assembly, conventions));
|
||||
}
|
||||
|
||||
public static ExtensionStore CreateStoreForCurrentDirectory<T>()
|
||||
{
|
||||
string assemblyPath = typeof(ExtensionStore).GetTypeInfo().Assembly.Location;
|
||||
string directory = Path.GetDirectoryName(assemblyPath);
|
||||
return new ExtensionStore(typeof(T), (conventions) =>
|
||||
new ContainerConfiguration().WithAssembliesInPath(directory, conventions));
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetExports<T>()
|
||||
{
|
||||
if (exports == null)
|
||||
{
|
||||
exports = host.GetExports(contractType).ToList();
|
||||
}
|
||||
return exports.Cast<T>();
|
||||
}
|
||||
|
||||
private ConventionBuilder GetExportBuilder()
|
||||
{
|
||||
// Define exports as matching a parent type, export as that parent type
|
||||
var builder = new ConventionBuilder();
|
||||
builder.ForTypesDerivedFrom(contractType).Export(exportConventionBuilder => exportConventionBuilder.AsContractType(contractType));
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ContainerConfigurationExtensions
|
||||
{
|
||||
public static ContainerConfiguration WithAssembliesInPath(this ContainerConfiguration configuration, string path, SearchOption searchOption = SearchOption.TopDirectoryOnly)
|
||||
{
|
||||
return WithAssembliesInPath(configuration, path, null, searchOption);
|
||||
}
|
||||
|
||||
public static ContainerConfiguration WithAssembliesInPath(this ContainerConfiguration configuration, string path, AttributedModelProvider conventions, SearchOption searchOption = SearchOption.TopDirectoryOnly)
|
||||
{
|
||||
AssemblyLoadContext context = new AssemblyLoader(path);
|
||||
var assemblyNames = Directory
|
||||
.GetFiles(path, "*.dll", searchOption)
|
||||
.Select(AssemblyLoadContext.GetAssemblyName);
|
||||
|
||||
var assemblies = assemblyNames
|
||||
.Select(context.LoadFromAssemblyName)
|
||||
.ToList();
|
||||
|
||||
configuration = configuration.WithAssemblies(assemblies, conventions);
|
||||
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
public class AssemblyLoader : AssemblyLoadContext
|
||||
{
|
||||
private string folderPath;
|
||||
|
||||
public AssemblyLoader(string folderPath)
|
||||
{
|
||||
this.folderPath = folderPath;
|
||||
}
|
||||
|
||||
protected override Assembly Load(AssemblyName assemblyName)
|
||||
{
|
||||
var deps = DependencyContext.Default;
|
||||
var res = deps.CompileLibraries.Where(d => d.Name.Equals(assemblyName.Name)).ToList();
|
||||
if (res.Count > 0)
|
||||
{
|
||||
return Assembly.Load(new AssemblyName(res.First().Name));
|
||||
}
|
||||
else
|
||||
{
|
||||
var apiApplicationFileInfo = new FileInfo($"{folderPath}{Path.DirectorySeparatorChar}{assemblyName.Name}.dll");
|
||||
if (File.Exists(apiApplicationFileInfo.FullName))
|
||||
{
|
||||
var asl = new AssemblyLoader(apiApplicationFileInfo.DirectoryName);
|
||||
return asl.LoadFromAssemblyPath(apiApplicationFileInfo.FullName);
|
||||
}
|
||||
}
|
||||
return Assembly.Load(assemblyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// A Service that expects to lookup other services. Using this interface on an exported service
|
||||
/// will ensure the <see cref="SetServiceProvider(IMultiServiceProvider)"/> method is called during
|
||||
/// service initialization
|
||||
/// </summary>
|
||||
public interface IComposableService
|
||||
{
|
||||
/// <summary>
|
||||
/// Supports settings the service provider being used to initialize the service.
|
||||
/// This is useful to look up other services and use them in your own service.
|
||||
/// </summary>
|
||||
void SetServiceProvider(IMultiServiceProvider provider);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Extensibility
|
||||
{
|
||||
|
||||
internal static class IEnumerableExt
|
||||
{
|
||||
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// 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.Linq;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Extensibility
|
||||
{
|
||||
public interface IMultiServiceProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a service of a specific type. It is expected that only 1 instance of this type will be
|
||||
/// available
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of service to be found</typeparam>
|
||||
/// <returns>Instance of T or null if not found</returns>
|
||||
/// <exception cref="InvalidOperationException">The input sequence contains more than one element.-or-The input sequence is empty.</exception>
|
||||
T GetService<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a service of a specific type. The first service matching the specified filter will be returned
|
||||
/// available
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of service to be found</typeparam>
|
||||
/// <param name="filter">Filter to use in </param>
|
||||
/// <returns>Instance of T or null if not found</returns>
|
||||
/// <exception cref="InvalidOperationException">The input sequence contains more than one element.-or-The input sequence is empty.</exception>
|
||||
T GetService<T>(Predicate<T> filter);
|
||||
|
||||
/// <summary>
|
||||
/// Gets multiple services of a given type
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>An enumerable of matching services</returns>
|
||||
IEnumerable<T> GetServices<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets multiple services of a given type, where they match a filter
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="filter"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<T> GetServices<T>(Predicate<T> filter);
|
||||
}
|
||||
|
||||
|
||||
public abstract class ServiceProviderBase : IMultiServiceProvider
|
||||
{
|
||||
|
||||
public T GetService<T>()
|
||||
{
|
||||
return GetServices<T>().SingleOrDefault();
|
||||
}
|
||||
|
||||
public T GetService<T>(Predicate<T> filter)
|
||||
{
|
||||
Validate.IsNotNull(nameof(filter), filter);
|
||||
return GetServices<T>().Where(t => filter(t)).SingleOrDefault();
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetServices<T>(Predicate<T> filter)
|
||||
{
|
||||
Validate.IsNotNull(nameof(filter), filter);
|
||||
return GetServices<T>().Where(t => filter(t));
|
||||
}
|
||||
|
||||
public virtual IEnumerable<T> GetServices<T>()
|
||||
{
|
||||
var services = GetServicesImpl<T>();
|
||||
if (services == null)
|
||||
{
|
||||
return Enumerable.Empty<T>();
|
||||
}
|
||||
|
||||
return services.Select(t =>
|
||||
{
|
||||
InitComposableService(t);
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
private void InitComposableService<T>(T t)
|
||||
{
|
||||
IComposableService c = t as IComposableService;
|
||||
if (c != null)
|
||||
{
|
||||
c.SetServiceProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all services using the build in implementation
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
protected abstract IEnumerable<T> GetServicesImpl<T>();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.SqlTools.Hosting;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Extensibility
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A service provider implementation that allows registering of specific services
|
||||
/// </summary>
|
||||
public class RegisteredServiceProvider : ServiceProviderBase
|
||||
{
|
||||
public delegate IEnumerable ServiceLookup();
|
||||
|
||||
protected Dictionary<Type, ServiceLookup> services = new Dictionary<Type, ServiceLookup>();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a singular service to be returned during lookup
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>this provider, to simplify fluent declarations</returns>
|
||||
/// <exception cref="ArgumentNullException">If service is null</exception>
|
||||
/// <exception cref="InvalidOperationException">If an existing service is already registered</exception>
|
||||
public RegisteredServiceProvider RegisterSingleService<T>(T service)
|
||||
{
|
||||
Validate.IsNotNull(nameof(service), service);
|
||||
ThrowIfAlreadyRegistered<T>();
|
||||
services.Add(typeof(T), () => service.SingleItemAsEnumerable());
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a singular service to be returned during lookup
|
||||
/// </summary>
|
||||
/// <param name="type">
|
||||
/// Type or interface this service should be registed as. Any <see cref="IMultiServiceProvider.GetServices{T}"/> request
|
||||
/// for that type will return this service
|
||||
/// </param>
|
||||
/// <param name="service">service object to be added</param>
|
||||
/// <returns>this provider, to simplify fluent declarations</returns>
|
||||
/// <exception cref="ArgumentNullException">If service is null</exception>
|
||||
/// <exception cref="InvalidOperationException">If an existing service is already registered</exception>
|
||||
public RegisteredServiceProvider RegisterSingleService(Type type, object service)
|
||||
{
|
||||
Validate.IsNotNull(nameof(type), type);
|
||||
Validate.IsNotNull(nameof(service), service);
|
||||
ThrowIfAlreadyRegistered(type);
|
||||
ThrowIfIncompatible(type, service);
|
||||
services.Add(type, () => service.SingleItemAsEnumerable());
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a function that can look up multiple services
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>this provider, to simplify fluent declarations</returns>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="serviceLookup"/> is null</exception>
|
||||
/// <exception cref="InvalidOperationException">If an existing service is already registered</exception>
|
||||
public RegisteredServiceProvider Register<T>(Func<IEnumerable<T>> serviceLookup)
|
||||
{
|
||||
Validate.IsNotNull(nameof(serviceLookup), serviceLookup);
|
||||
ThrowIfAlreadyRegistered<T>();
|
||||
services.Add(typeof(T), () => serviceLookup());
|
||||
return this;
|
||||
}
|
||||
|
||||
private void ThrowIfAlreadyRegistered<T>()
|
||||
{
|
||||
ThrowIfAlreadyRegistered(typeof(T));
|
||||
}
|
||||
|
||||
private void ThrowIfAlreadyRegistered(Type type)
|
||||
{
|
||||
if (services.ContainsKey(type))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ServiceAlreadyRegistered, type.Name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ThrowIfIncompatible(Type type, object service)
|
||||
{
|
||||
if (!type.IsInstanceOfType(service))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ServiceNotOfExpectedType, service.GetType().Name, type.Name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected override IEnumerable<T> GetServicesImpl<T>()
|
||||
{
|
||||
ServiceLookup serviceLookup;
|
||||
if (services.TryGetValue(typeof(T), out serviceLookup))
|
||||
{
|
||||
return serviceLookup().Cast<T>();
|
||||
}
|
||||
return Enumerable.Empty<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a class that describes the capabilities of a language
|
||||
/// client. At this time no specific capabilities are listed for
|
||||
/// clients.
|
||||
/// </summary>
|
||||
public class ClientCapabilities
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// 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.Hosting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters to be used for reporting hosting-level errors, such as protocol violations
|
||||
/// </summary>
|
||||
public class HostingErrorParams
|
||||
{
|
||||
/// <summary>
|
||||
/// The message of the error
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class HostingErrorEvent
|
||||
{
|
||||
public static readonly
|
||||
EventType<HostingErrorParams> Type =
|
||||
EventType<HostingErrorParams>.Create("hosting/error");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// 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.Hosting.Contracts
|
||||
{
|
||||
public class InitializeRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<InitializeRequest, InitializeResult> Type =
|
||||
RequestType<InitializeRequest, InitializeResult>.Create("initialize");
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the root path of the editor's open workspace.
|
||||
/// If null it is assumed that a file was opened without having
|
||||
/// a workspace open.
|
||||
/// </summary>
|
||||
public string RootPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the capabilities provided by the client (editor).
|
||||
/// </summary>
|
||||
public ClientCapabilities Capabilities { get; set; }
|
||||
}
|
||||
|
||||
public class InitializeResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the capabilities provided by the language server.
|
||||
/// </summary>
|
||||
public ServerCapabilities Capabilities { get; set; }
|
||||
}
|
||||
|
||||
public class InitializeError
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the client should retry
|
||||
/// sending the Initialize request after showing the error to the user.
|
||||
/// </summary>
|
||||
public bool Retry { get; set;}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Contracts
|
||||
{
|
||||
public class ServerCapabilities
|
||||
{
|
||||
public TextDocumentSyncKind? TextDocumentSync { get; set; }
|
||||
|
||||
public bool? HoverProvider { get; set; }
|
||||
|
||||
public CompletionOptions CompletionProvider { get; set; }
|
||||
|
||||
public SignatureHelpOptions SignatureHelpProvider { get; set; }
|
||||
|
||||
public bool? DefinitionProvider { get; set; }
|
||||
|
||||
public bool? ReferencesProvider { get; set; }
|
||||
|
||||
public bool? DocumentHighlightProvider { get; set; }
|
||||
|
||||
public bool? DocumentFormattingProvider { get; set; }
|
||||
|
||||
public bool? DocumentRangeFormattingProvider { get; set; }
|
||||
|
||||
public bool? DocumentSymbolProvider { get; set; }
|
||||
|
||||
public bool? WorkspaceSymbolProvider { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the document synchronization strategies that a server may support.
|
||||
/// </summary>
|
||||
public enum TextDocumentSyncKind
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that documents should not be synced at all.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that document changes are always sent with the full content.
|
||||
/// </summary>
|
||||
Full,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that document changes are sent as incremental changes after
|
||||
/// the initial document content has been sent.
|
||||
/// </summary>
|
||||
Incremental
|
||||
}
|
||||
|
||||
public class CompletionOptions
|
||||
{
|
||||
public bool? ResolveProvider { get; set; }
|
||||
|
||||
public string[] TriggerCharacters { get; set; }
|
||||
}
|
||||
|
||||
public class SignatureHelpOptions
|
||||
{
|
||||
public string[] TriggerCharacters { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
32
src/Microsoft.SqlTools.Hosting/Hosting/Contracts/Shutdown.cs
Normal file
32
src/Microsoft.SqlTools.Hosting/Hosting/Contracts/Shutdown.cs
Normal file
@@ -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 Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a message that is sent from the client to request
|
||||
/// that the server shut down.
|
||||
/// </summary>
|
||||
public class ShutdownRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<object, object> Type =
|
||||
RequestType<object, object>.Create("shutdown");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines an event that is sent from the client to notify that
|
||||
/// the client is exiting and the server should as well.
|
||||
/// </summary>
|
||||
public class ExitNotification
|
||||
{
|
||||
public static readonly
|
||||
EventType<object> Type =
|
||||
EventType<object>.Create("exit");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// 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.Hosting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a message that is sent from the client to request
|
||||
/// the version of the server.
|
||||
/// </summary>
|
||||
public class VersionRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<object, string> Type =
|
||||
RequestType<object, string>.Create("version");
|
||||
}
|
||||
}
|
||||
71
src/Microsoft.SqlTools.Hosting/Hosting/IHostedService.cs
Normal file
71
src/Microsoft.SqlTools.Hosting/Hosting/IHostedService.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// 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 Microsoft.SqlTools.ServiceLayer.Extensibility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a hosted service that communicates with external processes via
|
||||
/// messages passed over the <see cref="ServiceHost"/>. The service defines
|
||||
/// a standard initialization method where it can hook up to the host.
|
||||
/// </summary>
|
||||
public interface IHostedService
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback to initialize this service
|
||||
/// </summary>
|
||||
/// <param name="serviceHost"><see cref="IProtocolEndpoint"/> which supports registering
|
||||
/// event handlers and other callbacks for messages passed to external callers</param>
|
||||
void InitializeService(IProtocolEndpoint serviceHost);
|
||||
|
||||
/// <summary>
|
||||
/// What is the service type that you wish to register?
|
||||
/// </summary>
|
||||
Type ServiceType { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for <see cref="IHostedService"/> implementations that handles defining the <see cref="ServiceType"/>
|
||||
/// being registered. This simplifies service registration. This also implements <see cref="IComposableService"/> which
|
||||
/// allows injection of the service provider for lookup of other services.
|
||||
///
|
||||
/// Extending classes should implement per below code example
|
||||
/// <code>
|
||||
/// [Export(typeof(IHostedService)]
|
||||
/// MyService : HostedService<MyService>
|
||||
/// {
|
||||
/// public override void InitializeService(IProtocolEndpoint serviceHost)
|
||||
/// {
|
||||
/// serviceHost.SetRequestHandler(MyRequest.Type, HandleMyRequest);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to be registered for lookup in the service provider</typeparam>
|
||||
public abstract class HostedService<T> : IHostedService, IComposableService
|
||||
{
|
||||
|
||||
protected IMultiServiceProvider ServiceProvider { get; private set; }
|
||||
|
||||
public void SetServiceProvider(IMultiServiceProvider provider)
|
||||
{
|
||||
ServiceProvider = provider;
|
||||
}
|
||||
|
||||
public Type ServiceType
|
||||
{
|
||||
get
|
||||
{
|
||||
return typeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void InitializeService(IProtocolEndpoint serviceHost);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a base implementation for servers and their clients over a
|
||||
/// single kind of communication channel.
|
||||
/// </summary>
|
||||
public abstract class ChannelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a boolean that is true if the channel is connected or false if not.
|
||||
/// </summary>
|
||||
public bool IsConnected { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MessageReader for reading messages from the channel.
|
||||
/// </summary>
|
||||
public MessageReader MessageReader { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MessageWriter for writing messages to the channel.
|
||||
/// </summary>
|
||||
public MessageWriter MessageWriter { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts the channel and initializes the MessageDispatcher.
|
||||
/// </summary>
|
||||
/// <param name="messageProtocolType">The type of message protocol used by the channel.</param>
|
||||
public void Start(MessageProtocolType messageProtocolType)
|
||||
{
|
||||
IMessageSerializer messageSerializer = null;
|
||||
if (messageProtocolType == MessageProtocolType.LanguageServer)
|
||||
{
|
||||
messageSerializer = new JsonRpcMessageSerializer();
|
||||
}
|
||||
else
|
||||
{
|
||||
messageSerializer = new V8MessageSerializer();
|
||||
}
|
||||
|
||||
this.Initialize(messageSerializer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Task that allows the consumer of the ChannelBase
|
||||
/// implementation to wait until a connection has been made to
|
||||
/// the opposite endpoint whether it's a client or server.
|
||||
/// </summary>
|
||||
/// <returns>A Task to be awaited until a connection is made.</returns>
|
||||
public abstract Task WaitForConnection();
|
||||
|
||||
/// <summary>
|
||||
/// Stops the channel.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
this.Shutdown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method to be implemented by subclasses to handle the
|
||||
/// actual initialization of the channel and the creation and
|
||||
/// assignment of the MessageReader and MessageWriter properties.
|
||||
/// </summary>
|
||||
/// <param name="messageSerializer">The IMessageSerializer to use for message serialization.</param>
|
||||
protected abstract void Initialize(IMessageSerializer messageSerializer);
|
||||
|
||||
/// <summary>
|
||||
/// A method to be implemented by subclasses to handle shutdown
|
||||
/// of the channel once Stop is called.
|
||||
/// </summary>
|
||||
protected abstract void Shutdown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a client implementation for the standard I/O channel.
|
||||
/// Launches the server process and then attaches to its console
|
||||
/// streams.
|
||||
/// </summary>
|
||||
public class StdioClientChannel : ChannelBase
|
||||
{
|
||||
private string serviceProcessPath;
|
||||
private string serviceProcessArguments;
|
||||
|
||||
private Stream inputStream;
|
||||
private Stream outputStream;
|
||||
private Process serviceProcess;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the process ID of the server process.
|
||||
/// </summary>
|
||||
public int ProcessId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of the StdioClient.
|
||||
/// </summary>
|
||||
/// <param name="serverProcessPath">The full path to the server process executable.</param>
|
||||
/// <param name="serverProcessArguments">Optional arguments to pass to the service process executable.</param>
|
||||
public StdioClientChannel(
|
||||
string serverProcessPath,
|
||||
params string[] serverProcessArguments)
|
||||
{
|
||||
this.serviceProcessPath = serverProcessPath;
|
||||
|
||||
if (serverProcessArguments != null)
|
||||
{
|
||||
this.serviceProcessArguments =
|
||||
string.Join(
|
||||
" ",
|
||||
serverProcessArguments);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Initialize(IMessageSerializer messageSerializer)
|
||||
{
|
||||
this.serviceProcess = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = this.serviceProcessPath,
|
||||
Arguments = this.serviceProcessArguments,
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
},
|
||||
EnableRaisingEvents = true,
|
||||
};
|
||||
|
||||
// Start the process
|
||||
this.serviceProcess.Start();
|
||||
this.ProcessId = this.serviceProcess.Id;
|
||||
|
||||
// Open the standard input/output streams
|
||||
this.inputStream = this.serviceProcess.StandardOutput.BaseStream;
|
||||
this.outputStream = this.serviceProcess.StandardInput.BaseStream;
|
||||
|
||||
// Set up the message reader and writer
|
||||
this.MessageReader =
|
||||
new MessageReader(
|
||||
this.inputStream,
|
||||
messageSerializer);
|
||||
|
||||
this.MessageWriter =
|
||||
new MessageWriter(
|
||||
this.outputStream,
|
||||
messageSerializer);
|
||||
|
||||
this.IsConnected = true;
|
||||
}
|
||||
|
||||
public override Task WaitForConnection()
|
||||
{
|
||||
// We're always connected immediately in the stdio channel
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
if (this.inputStream != null)
|
||||
{
|
||||
this.inputStream.Dispose();
|
||||
this.inputStream = null;
|
||||
}
|
||||
|
||||
if (this.outputStream != null)
|
||||
{
|
||||
this.outputStream.Dispose();
|
||||
this.outputStream = null;
|
||||
}
|
||||
|
||||
if (this.MessageReader != null)
|
||||
{
|
||||
this.MessageReader = null;
|
||||
}
|
||||
|
||||
if (this.MessageWriter != null)
|
||||
{
|
||||
this.MessageWriter = null;
|
||||
}
|
||||
|
||||
this.serviceProcess.Kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a server implementation for the standard I/O channel.
|
||||
/// When started in a process, attaches to the console I/O streams
|
||||
/// to communicate with the client that launched the process.
|
||||
/// </summary>
|
||||
public class StdioServerChannel : ChannelBase
|
||||
{
|
||||
private Stream inputStream;
|
||||
private Stream outputStream;
|
||||
|
||||
protected override void Initialize(IMessageSerializer messageSerializer)
|
||||
{
|
||||
#if !NanoServer
|
||||
// Ensure that the console is using UTF-8 encoding
|
||||
System.Console.InputEncoding = Encoding.UTF8;
|
||||
System.Console.OutputEncoding = Encoding.UTF8;
|
||||
#endif
|
||||
|
||||
// Open the standard input/output streams
|
||||
this.inputStream = System.Console.OpenStandardInput();
|
||||
this.outputStream = System.Console.OpenStandardOutput();
|
||||
|
||||
// Set up the reader and writer
|
||||
this.MessageReader =
|
||||
new MessageReader(
|
||||
this.inputStream,
|
||||
messageSerializer);
|
||||
|
||||
this.MessageWriter =
|
||||
new MessageWriter(
|
||||
this.outputStream,
|
||||
messageSerializer);
|
||||
|
||||
this.IsConnected = true;
|
||||
}
|
||||
|
||||
public override Task WaitForConnection()
|
||||
{
|
||||
// We're always connected immediately in the stdio channel
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
// No default implementation needed, streams will be
|
||||
// disposed on process shutdown.
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/Microsoft.SqlTools.Hosting/Hosting/Protocol/Constants.cs
Normal file
27
src/Microsoft.SqlTools.Hosting/Hosting/Protocol/Constants.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const string ContentLengthFormatString = "Content-Length: {0}\r\n\r\n";
|
||||
public static readonly JsonSerializerSettings JsonSerializerSettings;
|
||||
|
||||
public static readonly string SqlLoginAuthenticationType = "SqlLogin";
|
||||
|
||||
static Constants()
|
||||
{
|
||||
JsonSerializerSettings = new JsonSerializerSettings();
|
||||
|
||||
// Camel case all object properties
|
||||
JsonSerializerSettings.ContractResolver =
|
||||
new CamelCasePropertyNamesContractResolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an event type with a particular method name.
|
||||
/// </summary>
|
||||
/// <typeparam name="TParams">The parameter type for this event.</typeparam>
|
||||
public class EventType<TParams>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the method name for the event type.
|
||||
/// </summary>
|
||||
public string MethodName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an EventType instance with the given parameter type and method name.
|
||||
/// </summary>
|
||||
/// <param name="methodName">The method name of the event.</param>
|
||||
/// <returns>A new EventType instance for the defined type.</returns>
|
||||
public static EventType<TParams> Create(string methodName)
|
||||
{
|
||||
return new EventType<TParams>()
|
||||
{
|
||||
MethodName = methodName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Diagnostics;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines all possible message types.
|
||||
/// </summary>
|
||||
public enum MessageType
|
||||
{
|
||||
Unknown,
|
||||
Request,
|
||||
Response,
|
||||
Event
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides common details for protocol messages of any format.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("MessageType = {MessageType.ToString()}, Method = {Method}, Id = {Id}")]
|
||||
public class Message
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the message type.
|
||||
/// </summary>
|
||||
public MessageType MessageType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the message's sequence ID.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the message's method/command name.
|
||||
/// </summary>
|
||||
public string Method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a JToken containing the contents of the message.
|
||||
/// </summary>
|
||||
public JToken Contents { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a JToken containing error details.
|
||||
/// </summary>
|
||||
public JToken Error { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a message with an Unknown type.
|
||||
/// </summary>
|
||||
/// <returns>A message with Unknown type.</returns>
|
||||
public static Message Unknown()
|
||||
{
|
||||
return new Message
|
||||
{
|
||||
MessageType = MessageType.Unknown
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a message with a Request type.
|
||||
/// </summary>
|
||||
/// <param name="id">The sequence ID of the request.</param>
|
||||
/// <param name="method">The method name of the request.</param>
|
||||
/// <param name="contents">The contents of the request.</param>
|
||||
/// <returns>A message with a Request type.</returns>
|
||||
public static Message Request(string id, string method, JToken contents)
|
||||
{
|
||||
return new Message
|
||||
{
|
||||
MessageType = MessageType.Request,
|
||||
Id = id,
|
||||
Method = method,
|
||||
Contents = contents
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a message with a Response type.
|
||||
/// </summary>
|
||||
/// <param name="id">The sequence ID of the original request.</param>
|
||||
/// <param name="method">The method name of the original request.</param>
|
||||
/// <param name="contents">The contents of the response.</param>
|
||||
/// <returns>A message with a Response type.</returns>
|
||||
public static Message Response(string id, string method, JToken contents)
|
||||
{
|
||||
return new Message
|
||||
{
|
||||
MessageType = MessageType.Response,
|
||||
Id = id,
|
||||
Method = method,
|
||||
Contents = contents
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a message with a Response type and error details.
|
||||
/// </summary>
|
||||
/// <param name="id">The sequence ID of the original request.</param>
|
||||
/// <param name="method">The method name of the original request.</param>
|
||||
/// <param name="error">The error details of the response.</param>
|
||||
/// <returns>A message with a Response type and error details.</returns>
|
||||
public static Message ResponseError(string id, string method, JToken error)
|
||||
{
|
||||
return new Message
|
||||
{
|
||||
MessageType = MessageType.Response,
|
||||
Id = id,
|
||||
Method = method,
|
||||
Error = error
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a message with an Event type.
|
||||
/// </summary>
|
||||
/// <param name="method">The method name of the event.</param>
|
||||
/// <param name="contents">The contents of the event.</param>
|
||||
/// <returns>A message with an Event type.</returns>
|
||||
public static Message Event(string method, JToken contents)
|
||||
{
|
||||
return new Message
|
||||
{
|
||||
MessageType = MessageType.Event,
|
||||
Method = method,
|
||||
Contents = contents
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts
|
||||
{
|
||||
[DebuggerDisplay("RequestType MethodName = {MethodName}")]
|
||||
public class RequestType<TParams, TResult>
|
||||
{
|
||||
public string MethodName { get; private set; }
|
||||
|
||||
public static RequestType<TParams, TResult> Create(string typeName)
|
||||
{
|
||||
return new RequestType<TParams, TResult>()
|
||||
{
|
||||
MethodName = typeName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides context for a received event so that handlers
|
||||
/// can write events back to the channel.
|
||||
/// </summary>
|
||||
public class EventContext
|
||||
{
|
||||
private readonly MessageWriter messageWriter;
|
||||
|
||||
/// <summary>
|
||||
/// Parameterless constructor required for mocking
|
||||
/// </summary>
|
||||
public EventContext() { }
|
||||
|
||||
public EventContext(MessageWriter messageWriter)
|
||||
{
|
||||
this.messageWriter = messageWriter;
|
||||
}
|
||||
|
||||
public async Task SendEvent<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
TParams eventParams)
|
||||
{
|
||||
await this.messageWriter.WriteEvent(
|
||||
eventType,
|
||||
eventParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
public interface IEventSender
|
||||
{
|
||||
Task SendEvent<TParams>(EventType<TParams> eventType, TParams eventParams);
|
||||
}
|
||||
}
|
||||
@@ -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.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// A ProtocolEndpoint is used for inter-process communication. Services can register to
|
||||
/// respond to requests and events, send their own requests, and listen for notifications
|
||||
/// sent by the other side of the endpoint
|
||||
/// </summary>
|
||||
public interface IProtocolEndpoint : IEventSender, IRequestSender
|
||||
{
|
||||
void SetRequestHandler<TParams, TResult>(
|
||||
RequestType<TParams, TResult> requestType,
|
||||
Func<TParams, RequestContext<TResult>, Task> requestHandler);
|
||||
|
||||
void SetEventHandler<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
Func<TParams, EventContext, Task> eventHandler);
|
||||
|
||||
void SetEventHandler<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
Func<TParams, EventContext, Task> eventHandler,
|
||||
bool overrideExisting);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
public interface IRequestSender
|
||||
{
|
||||
Task<TResult> SendRequest<TParams, TResult>(RequestType<TParams, TResult> requestType, TParams requestParams,
|
||||
bool waitForResponse);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
//
|
||||
// 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.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
public class MessageDispatcher
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private ChannelBase protocolChannel;
|
||||
|
||||
private AsyncContextThread messageLoopThread;
|
||||
|
||||
internal Dictionary<string, Func<Message, MessageWriter, Task>> requestHandlers =
|
||||
new Dictionary<string, Func<Message, MessageWriter, Task>>();
|
||||
|
||||
internal Dictionary<string, Func<Message, MessageWriter, Task>> eventHandlers =
|
||||
new Dictionary<string, Func<Message, MessageWriter, Task>>();
|
||||
|
||||
private Action<Message> responseHandler;
|
||||
|
||||
private CancellationTokenSource messageLoopCancellationToken =
|
||||
new CancellationTokenSource();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public SynchronizationContext SynchronizationContext { get; private set; }
|
||||
|
||||
public bool InMessageLoopThread
|
||||
{
|
||||
get
|
||||
{
|
||||
// We're in the same thread as the message loop if the
|
||||
// current synchronization context equals the one we
|
||||
// know.
|
||||
return SynchronizationContext.Current == this.SynchronizationContext;
|
||||
}
|
||||
}
|
||||
|
||||
protected MessageReader MessageReader { get; private set; }
|
||||
|
||||
protected MessageWriter MessageWriter { get; private set; }
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public MessageDispatcher(ChannelBase protocolChannel)
|
||||
{
|
||||
this.protocolChannel = protocolChannel;
|
||||
this.MessageReader = protocolChannel.MessageReader;
|
||||
this.MessageWriter = protocolChannel.MessageWriter;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Start()
|
||||
{
|
||||
// Start the main message loop thread. The Task is
|
||||
// not explicitly awaited because it is running on
|
||||
// an independent background thread.
|
||||
this.messageLoopThread = new AsyncContextThread("Message Dispatcher");
|
||||
this.messageLoopThread
|
||||
.Run(() => this.ListenForMessages(this.messageLoopCancellationToken.Token))
|
||||
.ContinueWith(this.OnListenTaskCompleted);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
// Stop the message loop thread
|
||||
if (this.messageLoopThread != null)
|
||||
{
|
||||
this.messageLoopCancellationToken.Cancel();
|
||||
this.messageLoopThread.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRequestHandler<TParams, TResult>(
|
||||
RequestType<TParams, TResult> requestType,
|
||||
Func<TParams, RequestContext<TResult>, Task> requestHandler)
|
||||
{
|
||||
this.SetRequestHandler(
|
||||
requestType,
|
||||
requestHandler,
|
||||
false);
|
||||
}
|
||||
|
||||
public void SetRequestHandler<TParams, TResult>(
|
||||
RequestType<TParams, TResult> requestType,
|
||||
Func<TParams, RequestContext<TResult>, Task> requestHandler,
|
||||
bool overrideExisting)
|
||||
{
|
||||
if (overrideExisting)
|
||||
{
|
||||
// Remove the existing handler so a new one can be set
|
||||
this.requestHandlers.Remove(requestType.MethodName);
|
||||
}
|
||||
|
||||
this.requestHandlers.Add(
|
||||
requestType.MethodName,
|
||||
(requestMessage, messageWriter) =>
|
||||
{
|
||||
var requestContext =
|
||||
new RequestContext<TResult>(
|
||||
requestMessage,
|
||||
messageWriter);
|
||||
|
||||
TParams typedParams = default(TParams);
|
||||
if (requestMessage.Contents != null)
|
||||
{
|
||||
// TODO: Catch parse errors!
|
||||
typedParams = requestMessage.Contents.ToObject<TParams>();
|
||||
}
|
||||
|
||||
return requestHandler(typedParams, requestContext);
|
||||
});
|
||||
}
|
||||
|
||||
public void SetEventHandler<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
Func<TParams, EventContext, Task> eventHandler)
|
||||
{
|
||||
this.SetEventHandler(
|
||||
eventType,
|
||||
eventHandler,
|
||||
false);
|
||||
}
|
||||
|
||||
public void SetEventHandler<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
Func<TParams, EventContext, Task> eventHandler,
|
||||
bool overrideExisting)
|
||||
{
|
||||
if (overrideExisting)
|
||||
{
|
||||
// Remove the existing handler so a new one can be set
|
||||
this.eventHandlers.Remove(eventType.MethodName);
|
||||
}
|
||||
|
||||
this.eventHandlers.Add(
|
||||
eventType.MethodName,
|
||||
(eventMessage, messageWriter) =>
|
||||
{
|
||||
var eventContext = new EventContext(messageWriter);
|
||||
|
||||
TParams typedParams = default(TParams);
|
||||
if (eventMessage.Contents != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
typedParams = eventMessage.Contents.ToObject<TParams>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return eventHandler(typedParams, eventContext);
|
||||
});
|
||||
}
|
||||
|
||||
public void SetResponseHandler(Action<Message> responseHandler)
|
||||
{
|
||||
this.responseHandler = responseHandler;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<Exception> UnhandledException;
|
||||
|
||||
protected void OnUnhandledException(Exception unhandledException)
|
||||
{
|
||||
if (this.UnhandledException != null)
|
||||
{
|
||||
this.UnhandledException(this, unhandledException);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task ListenForMessages(CancellationToken cancellationToken)
|
||||
{
|
||||
this.SynchronizationContext = SynchronizationContext.Current;
|
||||
|
||||
// Run the message loop
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
Message newMessage;
|
||||
|
||||
try
|
||||
{
|
||||
// Read a message from the channel
|
||||
newMessage = await this.MessageReader.ReadMessage();
|
||||
}
|
||||
catch (MessageParseException e)
|
||||
{
|
||||
string message = string.Format("Exception occurred while parsing message: {0}", e.Message);
|
||||
Logger.Write(LogLevel.Error, message);
|
||||
await MessageWriter.WriteEvent(HostingErrorEvent.Type, new HostingErrorParams { Message = message });
|
||||
|
||||
// Continue the loop
|
||||
continue;
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
{
|
||||
// The stream has ended, end the message loop
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Log the error and send an error event to the client
|
||||
string message = string.Format("Exception occurred while receiving message: {0}", e.Message);
|
||||
Logger.Write(LogLevel.Error, message);
|
||||
await MessageWriter.WriteEvent(HostingErrorEvent.Type, new HostingErrorParams { Message = message });
|
||||
|
||||
// Continue the loop
|
||||
continue;
|
||||
}
|
||||
|
||||
// The message could be null if there was an error parsing the
|
||||
// previous message. In this case, do not try to dispatch it.
|
||||
if (newMessage != null)
|
||||
{
|
||||
// Verbose logging
|
||||
string logMessage = string.Format("Received message of type[{0}] and method[{1}]",
|
||||
newMessage.MessageType, newMessage.Method);
|
||||
Logger.Write(LogLevel.Verbose, logMessage);
|
||||
|
||||
// Process the message
|
||||
await this.DispatchMessage(newMessage, this.MessageWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task DispatchMessage(
|
||||
Message messageToDispatch,
|
||||
MessageWriter messageWriter)
|
||||
{
|
||||
Task handlerToAwait = null;
|
||||
|
||||
if (messageToDispatch.MessageType == MessageType.Request)
|
||||
{
|
||||
Func<Message, MessageWriter, Task> requestHandler = null;
|
||||
if (this.requestHandlers.TryGetValue(messageToDispatch.Method, out requestHandler))
|
||||
{
|
||||
handlerToAwait = requestHandler(messageToDispatch, messageWriter);
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// // TODO: Message not supported error
|
||||
// }
|
||||
}
|
||||
else if (messageToDispatch.MessageType == MessageType.Response)
|
||||
{
|
||||
if (this.responseHandler != null)
|
||||
{
|
||||
this.responseHandler(messageToDispatch);
|
||||
}
|
||||
}
|
||||
else if (messageToDispatch.MessageType == MessageType.Event)
|
||||
{
|
||||
Func<Message, MessageWriter, Task> eventHandler = null;
|
||||
if (this.eventHandlers.TryGetValue(messageToDispatch.Method, out eventHandler))
|
||||
{
|
||||
handlerToAwait = eventHandler(messageToDispatch, messageWriter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Message not supported error
|
||||
}
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// // TODO: Return message not supported
|
||||
// }
|
||||
|
||||
if (handlerToAwait != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await handlerToAwait;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// Some tasks may be cancelled due to legitimate
|
||||
// timeouts so don't let those exceptions go higher.
|
||||
}
|
||||
catch (AggregateException e)
|
||||
{
|
||||
if (!(e.InnerExceptions[0] is TaskCanceledException))
|
||||
{
|
||||
// Cancelled tasks aren't a problem, so rethrow
|
||||
// anything that isn't a TaskCanceledException
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnListenTaskCompleted(Task listenTask)
|
||||
{
|
||||
if (listenTask.IsFaulted)
|
||||
{
|
||||
this.OnUnhandledException(listenTask.Exception);
|
||||
}
|
||||
else if (listenTask.IsCompleted || listenTask.IsCanceled)
|
||||
{
|
||||
// TODO: Dispose of anything?
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
public class MessageParseException : Exception
|
||||
{
|
||||
public string OriginalMessageText { get; private set; }
|
||||
|
||||
public MessageParseException(
|
||||
string originalMessageText,
|
||||
string errorMessage,
|
||||
params object[] errorMessageArgs)
|
||||
: base(string.Format(errorMessage, errorMessageArgs))
|
||||
{
|
||||
this.OriginalMessageText = originalMessageText;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the possible message protocol types.
|
||||
/// </summary>
|
||||
public enum MessageProtocolType
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the language server message protocol.
|
||||
/// </summary>
|
||||
LanguageServer,
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the debug adapter message protocol.
|
||||
/// </summary>
|
||||
DebugAdapter
|
||||
}
|
||||
}
|
||||
273
src/Microsoft.SqlTools.Hosting/Hosting/Protocol/MessageReader.cs
Normal file
273
src/Microsoft.SqlTools.Hosting/Hosting/Protocol/MessageReader.cs
Normal file
@@ -0,0 +1,273 @@
|
||||
//
|
||||
// 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.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Hosting;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
public class MessageReader
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
public const int DefaultBufferSize = 8192;
|
||||
public const double BufferResizeTrigger = 0.25;
|
||||
|
||||
private const int CR = 0x0D;
|
||||
private const int LF = 0x0A;
|
||||
private static readonly string[] NewLineDelimiters = { Environment.NewLine };
|
||||
|
||||
private readonly Stream inputStream;
|
||||
private readonly IMessageSerializer messageSerializer;
|
||||
private readonly Encoding messageEncoding;
|
||||
|
||||
private ReadState readState;
|
||||
private bool needsMoreData = true;
|
||||
private int readOffset;
|
||||
private int bufferEndOffset;
|
||||
private byte[] messageBuffer;
|
||||
|
||||
private int expectedContentLength;
|
||||
private Dictionary<string, string> messageHeaders;
|
||||
|
||||
private enum ReadState
|
||||
{
|
||||
Headers,
|
||||
Content
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public MessageReader(
|
||||
Stream inputStream,
|
||||
IMessageSerializer messageSerializer,
|
||||
Encoding messageEncoding = null)
|
||||
{
|
||||
Validate.IsNotNull("streamReader", inputStream);
|
||||
Validate.IsNotNull("messageSerializer", messageSerializer);
|
||||
|
||||
this.inputStream = inputStream;
|
||||
this.messageSerializer = messageSerializer;
|
||||
|
||||
this.messageEncoding = messageEncoding;
|
||||
if (messageEncoding == null)
|
||||
{
|
||||
this.messageEncoding = Encoding.UTF8;
|
||||
}
|
||||
|
||||
this.messageBuffer = new byte[DefaultBufferSize];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public async Task<Message> ReadMessage()
|
||||
{
|
||||
string messageContent = null;
|
||||
|
||||
// Do we need to read more data or can we process the existing buffer?
|
||||
while (!this.needsMoreData || await this.ReadNextChunk())
|
||||
{
|
||||
// Clear the flag since we should have what we need now
|
||||
this.needsMoreData = false;
|
||||
|
||||
// Do we need to look for message headers?
|
||||
if (this.readState == ReadState.Headers &&
|
||||
!this.TryReadMessageHeaders())
|
||||
{
|
||||
// If we don't have enough data to read headers yet, keep reading
|
||||
this.needsMoreData = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do we need to look for message content?
|
||||
if (this.readState == ReadState.Content &&
|
||||
!this.TryReadMessageContent(out messageContent))
|
||||
{
|
||||
// If we don't have enough data yet to construct the content, keep reading
|
||||
this.needsMoreData = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We've read a message now, break out of the loop
|
||||
break;
|
||||
}
|
||||
|
||||
// Now that we have a message, reset the buffer's state
|
||||
ShiftBufferBytesAndShrink(readOffset);
|
||||
|
||||
// Get the JObject for the JSON content
|
||||
JObject messageObject = JObject.Parse(messageContent);
|
||||
|
||||
// Return the parsed message
|
||||
return this.messageSerializer.DeserializeMessage(messageObject);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task<bool> ReadNextChunk()
|
||||
{
|
||||
// Do we need to resize the buffer? See if less than 1/4 of the space is left.
|
||||
if (((double)(this.messageBuffer.Length - this.bufferEndOffset) / this.messageBuffer.Length) < 0.25)
|
||||
{
|
||||
// Double the size of the buffer
|
||||
Array.Resize(
|
||||
ref this.messageBuffer,
|
||||
this.messageBuffer.Length * 2);
|
||||
}
|
||||
|
||||
// Read the next chunk into the message buffer
|
||||
int readLength =
|
||||
await this.inputStream.ReadAsync(
|
||||
this.messageBuffer,
|
||||
this.bufferEndOffset,
|
||||
this.messageBuffer.Length - this.bufferEndOffset);
|
||||
|
||||
this.bufferEndOffset += readLength;
|
||||
|
||||
if (readLength == 0)
|
||||
{
|
||||
// If ReadAsync returns 0 then it means that the stream was
|
||||
// closed unexpectedly (usually due to the client application
|
||||
// ending suddenly). For now, just terminate the language
|
||||
// server immediately.
|
||||
// TODO: Provide a more graceful shutdown path
|
||||
throw new EndOfStreamException(SR.HostingUnexpectedEndOfStream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryReadMessageHeaders()
|
||||
{
|
||||
int scanOffset = this.readOffset;
|
||||
|
||||
// Scan for the final double-newline that marks the end of the header lines
|
||||
while (scanOffset + 3 < this.bufferEndOffset &&
|
||||
(this.messageBuffer[scanOffset] != CR ||
|
||||
this.messageBuffer[scanOffset + 1] != LF ||
|
||||
this.messageBuffer[scanOffset + 2] != CR ||
|
||||
this.messageBuffer[scanOffset + 3] != LF))
|
||||
{
|
||||
scanOffset++;
|
||||
}
|
||||
|
||||
// Make sure we haven't reached the end of the buffer without finding a separator (e.g CRLFCRLF)
|
||||
if (scanOffset + 3 >= this.bufferEndOffset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the header block into a array of lines
|
||||
var headers = Encoding.ASCII.GetString(this.messageBuffer, this.readOffset, scanOffset)
|
||||
.Split(NewLineDelimiters, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
try
|
||||
{
|
||||
// Read each header and store it in the dictionary
|
||||
this.messageHeaders = new Dictionary<string, string>();
|
||||
foreach (var header in headers)
|
||||
{
|
||||
int currentLength = header.IndexOf(':');
|
||||
if (currentLength == -1)
|
||||
{
|
||||
throw new ArgumentException(SR.HostingHeaderMissingColon);
|
||||
}
|
||||
|
||||
var key = header.Substring(0, currentLength);
|
||||
var value = header.Substring(currentLength + 1).Trim();
|
||||
this.messageHeaders[key] = value;
|
||||
}
|
||||
|
||||
// Parse out the content length as an int
|
||||
string contentLengthString;
|
||||
if (!this.messageHeaders.TryGetValue("Content-Length", out contentLengthString))
|
||||
{
|
||||
throw new MessageParseException("", SR.HostingHeaderMissingContentLengthHeader);
|
||||
}
|
||||
|
||||
// Parse the content length to an integer
|
||||
if (!int.TryParse(contentLengthString, out this.expectedContentLength))
|
||||
{
|
||||
throw new MessageParseException("", SR.HostingHeaderMissingContentLengthValue);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// The content length was invalid or missing. Trash the buffer we've read
|
||||
ShiftBufferBytesAndShrink(scanOffset + 4);
|
||||
throw;
|
||||
}
|
||||
|
||||
// Skip past the headers plus the newline characters
|
||||
this.readOffset += scanOffset + 4;
|
||||
|
||||
// Done reading headers, now read content
|
||||
this.readState = ReadState.Content;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryReadMessageContent(out string messageContent)
|
||||
{
|
||||
messageContent = null;
|
||||
|
||||
// Do we have enough bytes to reach the expected length?
|
||||
if ((this.bufferEndOffset - this.readOffset) < this.expectedContentLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the message contents to a string using the specified encoding
|
||||
messageContent = this.messageEncoding.GetString(
|
||||
this.messageBuffer,
|
||||
this.readOffset,
|
||||
this.expectedContentLength);
|
||||
|
||||
readOffset += expectedContentLength;
|
||||
|
||||
// Done reading content, now look for headers for the next message
|
||||
this.readState = ReadState.Headers;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ShiftBufferBytesAndShrink(int bytesToRemove)
|
||||
{
|
||||
// Create a new buffer that is shrunken by the number of bytes to remove
|
||||
// Note: by using Max, we can guarantee a buffer of at least default buffer size
|
||||
byte[] newBuffer = new byte[Math.Max(messageBuffer.Length - bytesToRemove, DefaultBufferSize)];
|
||||
|
||||
// If we need to do shifting, do the shifting
|
||||
if (bytesToRemove <= messageBuffer.Length)
|
||||
{
|
||||
// Copy the existing buffer starting at the offset to remove
|
||||
Buffer.BlockCopy(messageBuffer, bytesToRemove, newBuffer, 0, bufferEndOffset - bytesToRemove);
|
||||
}
|
||||
|
||||
// Make the new buffer the message buffer
|
||||
messageBuffer = newBuffer;
|
||||
|
||||
// Reset the read offset and the end offset
|
||||
readOffset = 0;
|
||||
bufferEndOffset -= bytesToRemove;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
142
src/Microsoft.SqlTools.Hosting/Hosting/Protocol/MessageWriter.cs
Normal file
142
src/Microsoft.SqlTools.Hosting/Hosting/Protocol/MessageWriter.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
public class MessageWriter
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private Stream outputStream;
|
||||
private IMessageSerializer messageSerializer;
|
||||
private AsyncLock writeLock = new AsyncLock();
|
||||
|
||||
private JsonSerializer contentSerializer =
|
||||
JsonSerializer.Create(
|
||||
Constants.JsonSerializerSettings);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public MessageWriter(
|
||||
Stream outputStream,
|
||||
IMessageSerializer messageSerializer)
|
||||
{
|
||||
Validate.IsNotNull("streamWriter", outputStream);
|
||||
Validate.IsNotNull("messageSerializer", messageSerializer);
|
||||
|
||||
this.outputStream = outputStream;
|
||||
this.messageSerializer = messageSerializer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
// TODO: This method should be made protected or private
|
||||
|
||||
public async Task WriteMessage(Message messageToWrite)
|
||||
{
|
||||
Validate.IsNotNull("messageToWrite", messageToWrite);
|
||||
|
||||
// Serialize the message
|
||||
JObject messageObject =
|
||||
this.messageSerializer.SerializeMessage(
|
||||
messageToWrite);
|
||||
|
||||
// Log the JSON representation of the message
|
||||
Logger.Write(
|
||||
LogLevel.Verbose,
|
||||
string.Format(
|
||||
"WRITE MESSAGE:\r\n\r\n{0}",
|
||||
JsonConvert.SerializeObject(
|
||||
messageObject,
|
||||
Formatting.Indented,
|
||||
Constants.JsonSerializerSettings)));
|
||||
|
||||
string serializedMessage =
|
||||
JsonConvert.SerializeObject(
|
||||
messageObject,
|
||||
Constants.JsonSerializerSettings);
|
||||
|
||||
byte[] messageBytes = Encoding.UTF8.GetBytes(serializedMessage);
|
||||
byte[] headerBytes =
|
||||
Encoding.ASCII.GetBytes(
|
||||
string.Format(
|
||||
Constants.ContentLengthFormatString,
|
||||
messageBytes.Length));
|
||||
|
||||
// Make sure only one call is writing at a time. You might be thinking
|
||||
// "Why not use a normal lock?" We use an AsyncLock here so that the
|
||||
// message loop doesn't get blocked while waiting for I/O to complete.
|
||||
using (await this.writeLock.LockAsync())
|
||||
{
|
||||
// Send the message
|
||||
await this.outputStream.WriteAsync(headerBytes, 0, headerBytes.Length);
|
||||
await this.outputStream.WriteAsync(messageBytes, 0, messageBytes.Length);
|
||||
await this.outputStream.FlushAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WriteRequest<TParams, TResult>(
|
||||
RequestType<TParams, TResult> requestType,
|
||||
TParams requestParams,
|
||||
int requestId)
|
||||
{
|
||||
// Allow null content
|
||||
JToken contentObject =
|
||||
requestParams != null ?
|
||||
JToken.FromObject(requestParams, contentSerializer) :
|
||||
null;
|
||||
|
||||
await this.WriteMessage(
|
||||
Message.Request(
|
||||
requestId.ToString(),
|
||||
requestType.MethodName,
|
||||
contentObject));
|
||||
}
|
||||
|
||||
public async Task WriteResponse<TResult>(TResult resultContent, string method, string requestId)
|
||||
{
|
||||
// Allow null content
|
||||
JToken contentObject =
|
||||
resultContent != null ?
|
||||
JToken.FromObject(resultContent, contentSerializer) :
|
||||
null;
|
||||
|
||||
await this.WriteMessage(
|
||||
Message.Response(
|
||||
requestId,
|
||||
method,
|
||||
contentObject));
|
||||
}
|
||||
|
||||
public async Task WriteEvent<TParams>(EventType<TParams> eventType, TParams eventParams)
|
||||
{
|
||||
// Allow null content
|
||||
JToken contentObject =
|
||||
eventParams != null ?
|
||||
JToken.FromObject(eventParams, contentSerializer) :
|
||||
null;
|
||||
|
||||
await this.WriteMessage(
|
||||
Message.Event(
|
||||
eventType.MethodName,
|
||||
contentObject));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
//
|
||||
// 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides behavior for a client or server endpoint that
|
||||
/// communicates using the specified protocol.
|
||||
/// </summary>
|
||||
public class ProtocolEndpoint : IProtocolEndpoint
|
||||
{
|
||||
private bool isInitialized;
|
||||
private bool isStarted;
|
||||
private int currentMessageId;
|
||||
private ChannelBase protocolChannel;
|
||||
private MessageProtocolType messageProtocolType;
|
||||
private TaskCompletionSource<bool> endpointExitedTask;
|
||||
private SynchronizationContext originalSynchronizationContext;
|
||||
|
||||
private Dictionary<string, TaskCompletionSource<Message>> pendingRequests =
|
||||
new Dictionary<string, TaskCompletionSource<Message>>();
|
||||
|
||||
/// <summary>
|
||||
/// When true, SendEvent will ignore exceptions and write them
|
||||
/// to the log instead. Intended to be used for test scenarios
|
||||
/// where SendEvent throws exceptions unrelated to what is
|
||||
/// being tested.
|
||||
/// </summary>
|
||||
internal static bool SendEventIgnoreExceptions = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MessageDispatcher which allows registration of
|
||||
/// handlers for requests, responses, and events that are
|
||||
/// transmitted through the channel.
|
||||
/// </summary>
|
||||
protected MessageDispatcher MessageDispatcher { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of the protocol server using the
|
||||
/// specified channel for communication.
|
||||
/// </summary>
|
||||
/// <param name="protocolChannel">
|
||||
/// The channel to use for communication with the connected endpoint.
|
||||
/// </param>
|
||||
/// <param name="messageProtocolType">
|
||||
/// The type of message protocol used by the endpoint.
|
||||
/// </param>
|
||||
public ProtocolEndpoint(
|
||||
ChannelBase protocolChannel,
|
||||
MessageProtocolType messageProtocolType)
|
||||
{
|
||||
this.protocolChannel = protocolChannel;
|
||||
this.messageProtocolType = messageProtocolType;
|
||||
this.originalSynchronizationContext = SynchronizationContext.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
if (!this.isInitialized)
|
||||
{
|
||||
// Start the provided protocol channel
|
||||
this.protocolChannel.Start(this.messageProtocolType);
|
||||
|
||||
// Start the message dispatcher
|
||||
this.MessageDispatcher = new MessageDispatcher(this.protocolChannel);
|
||||
|
||||
// Set the handler for any message responses that come back
|
||||
this.MessageDispatcher.SetResponseHandler(this.HandleResponse);
|
||||
|
||||
// Listen for unhandled exceptions from the dispatcher
|
||||
this.MessageDispatcher.UnhandledException += MessageDispatcher_UnhandledException;
|
||||
|
||||
this.isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the language server client and sends the Initialize method.
|
||||
/// </summary>
|
||||
/// <returns>A Task that can be awaited for initialization to complete.</returns>
|
||||
public async Task Start()
|
||||
{
|
||||
if (!this.isStarted)
|
||||
{
|
||||
|
||||
// Notify implementation about endpoint start
|
||||
await this.OnStart();
|
||||
|
||||
// Wait for connection and notify the implementor
|
||||
// NOTE: This task is not meant to be awaited.
|
||||
Task waitTask =
|
||||
this.protocolChannel
|
||||
.WaitForConnection()
|
||||
.ContinueWith(
|
||||
async (t) =>
|
||||
{
|
||||
// Start the MessageDispatcher
|
||||
this.MessageDispatcher.Start();
|
||||
await this.OnConnect();
|
||||
});
|
||||
|
||||
// Endpoint is now started
|
||||
this.isStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void WaitForExit()
|
||||
{
|
||||
this.endpointExitedTask = new TaskCompletionSource<bool>();
|
||||
this.endpointExitedTask.Task.Wait();
|
||||
}
|
||||
|
||||
public async Task Stop()
|
||||
{
|
||||
if (this.isStarted)
|
||||
{
|
||||
// Make sure no future calls try to stop the endpoint during shutdown
|
||||
this.isStarted = false;
|
||||
|
||||
// Stop the implementation first
|
||||
await this.OnStop();
|
||||
|
||||
// Stop the dispatcher and channel
|
||||
this.MessageDispatcher.Stop();
|
||||
this.protocolChannel.Stop();
|
||||
|
||||
// Notify anyone waiting for exit
|
||||
if (this.endpointExitedTask != null)
|
||||
{
|
||||
this.endpointExitedTask.SetResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Message Sending
|
||||
|
||||
/// <summary>
|
||||
/// Sends a request to the server
|
||||
/// </summary>
|
||||
/// <typeparam name="TParams"></typeparam>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="requestType"></param>
|
||||
/// <param name="requestParams"></param>
|
||||
/// <returns></returns>
|
||||
public Task<TResult> SendRequest<TParams, TResult>(
|
||||
RequestType<TParams, TResult> requestType,
|
||||
TParams requestParams)
|
||||
{
|
||||
return this.SendRequest(requestType, requestParams, true);
|
||||
}
|
||||
|
||||
public async Task<TResult> SendRequest<TParams, TResult>(
|
||||
RequestType<TParams, TResult> requestType,
|
||||
TParams requestParams,
|
||||
bool waitForResponse)
|
||||
{
|
||||
if (!this.protocolChannel.IsConnected)
|
||||
{
|
||||
throw new InvalidOperationException("SendRequest called when ProtocolChannel was not yet connected");
|
||||
}
|
||||
|
||||
this.currentMessageId++;
|
||||
|
||||
TaskCompletionSource<Message> responseTask = null;
|
||||
|
||||
if (waitForResponse)
|
||||
{
|
||||
responseTask = new TaskCompletionSource<Message>();
|
||||
this.pendingRequests.Add(
|
||||
this.currentMessageId.ToString(),
|
||||
responseTask);
|
||||
}
|
||||
|
||||
await this.protocolChannel.MessageWriter.WriteRequest<TParams, TResult>(
|
||||
requestType,
|
||||
requestParams,
|
||||
this.currentMessageId);
|
||||
|
||||
if (responseTask != null)
|
||||
{
|
||||
var responseMessage = await responseTask.Task;
|
||||
|
||||
return
|
||||
responseMessage.Contents != null ?
|
||||
responseMessage.Contents.ToObject<TResult>() :
|
||||
default(TResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Better default value here?
|
||||
return default(TResult);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an event to the channel's endpoint.
|
||||
/// </summary>
|
||||
/// <typeparam name="TParams">The event parameter type.</typeparam>
|
||||
/// <param name="eventType">The type of event being sent.</param>
|
||||
/// <param name="eventParams">The event parameters being sent.</param>
|
||||
/// <returns>A Task that tracks completion of the send operation.</returns>
|
||||
public Task SendEvent<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
TParams eventParams)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!this.protocolChannel.IsConnected)
|
||||
{
|
||||
throw new InvalidOperationException("SendEvent called when ProtocolChannel was not yet connected");
|
||||
}
|
||||
|
||||
// Some events could be raised from a different thread.
|
||||
// To ensure that messages are written serially, dispatch
|
||||
// dispatch the SendEvent call to the message loop thread.
|
||||
|
||||
if (!this.MessageDispatcher.InMessageLoopThread)
|
||||
{
|
||||
TaskCompletionSource<bool> writeTask = new TaskCompletionSource<bool>();
|
||||
|
||||
this.MessageDispatcher.SynchronizationContext.Post(
|
||||
async (obj) =>
|
||||
{
|
||||
await this.protocolChannel.MessageWriter.WriteEvent(
|
||||
eventType,
|
||||
eventParams);
|
||||
|
||||
writeTask.SetResult(true);
|
||||
}, null);
|
||||
|
||||
return writeTask.Task;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.protocolChannel.MessageWriter.WriteEvent(
|
||||
eventType,
|
||||
eventParams);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (SendEventIgnoreExceptions)
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, "Exception in SendEvent " + ex.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Message Handling
|
||||
|
||||
public void SetRequestHandler<TParams, TResult>(
|
||||
RequestType<TParams, TResult> requestType,
|
||||
Func<TParams, RequestContext<TResult>, Task> requestHandler)
|
||||
{
|
||||
this.MessageDispatcher.SetRequestHandler(
|
||||
requestType,
|
||||
requestHandler);
|
||||
}
|
||||
|
||||
public void SetEventHandler<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
Func<TParams, EventContext, Task> eventHandler)
|
||||
{
|
||||
this.MessageDispatcher.SetEventHandler(
|
||||
eventType,
|
||||
eventHandler,
|
||||
false);
|
||||
}
|
||||
|
||||
public void SetEventHandler<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
Func<TParams, EventContext, Task> eventHandler,
|
||||
bool overrideExisting)
|
||||
{
|
||||
this.MessageDispatcher.SetEventHandler(
|
||||
eventType,
|
||||
eventHandler,
|
||||
overrideExisting);
|
||||
}
|
||||
|
||||
private void HandleResponse(Message responseMessage)
|
||||
{
|
||||
TaskCompletionSource<Message> pendingRequestTask = null;
|
||||
|
||||
if (this.pendingRequests.TryGetValue(responseMessage.Id, out pendingRequestTask))
|
||||
{
|
||||
pendingRequestTask.SetResult(responseMessage);
|
||||
this.pendingRequests.Remove(responseMessage.Id);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Subclass Lifetime Methods
|
||||
|
||||
protected virtual Task OnStart()
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
protected virtual Task OnConnect()
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
protected virtual Task OnStop()
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void MessageDispatcher_UnhandledException(object sender, Exception e)
|
||||
{
|
||||
if (this.endpointExitedTask != null)
|
||||
{
|
||||
this.endpointExitedTask.SetException(e);
|
||||
}
|
||||
|
||||
else if (this.originalSynchronizationContext != null)
|
||||
{
|
||||
this.originalSynchronizationContext.Post(o => { throw e; }, null);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
{
|
||||
public class RequestContext<TResult> : IEventSender
|
||||
{
|
||||
private readonly Message requestMessage;
|
||||
private readonly MessageWriter messageWriter;
|
||||
|
||||
public RequestContext(Message requestMessage, MessageWriter messageWriter)
|
||||
{
|
||||
this.requestMessage = requestMessage;
|
||||
this.messageWriter = messageWriter;
|
||||
}
|
||||
|
||||
public RequestContext() { }
|
||||
|
||||
public virtual async Task SendResult(TResult resultDetails)
|
||||
{
|
||||
await this.messageWriter.WriteResponse(
|
||||
resultDetails,
|
||||
requestMessage.Method,
|
||||
requestMessage.Id);
|
||||
}
|
||||
|
||||
public virtual async Task SendEvent<TParams>(EventType<TParams> eventType, TParams eventParams)
|
||||
{
|
||||
await this.messageWriter.WriteEvent(
|
||||
eventType,
|
||||
eventParams);
|
||||
}
|
||||
|
||||
public virtual async Task SendError(object errorDetails)
|
||||
{
|
||||
await this.messageWriter.WriteMessage(
|
||||
Message.ResponseError(
|
||||
requestMessage.Id,
|
||||
requestMessage.Method,
|
||||
JToken.FromObject(errorDetails)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a common interface for message serializers.
|
||||
/// </summary>
|
||||
public interface IMessageSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes a Message to a JObject.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be serialized.</param>
|
||||
/// <returns>A JObject which contains the JSON representation of the message.</returns>
|
||||
JObject SerializeMessage(Message message);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a JObject to a Messsage.
|
||||
/// </summary>
|
||||
/// <param name="messageJson">The JObject containing the JSON representation of the message.</param>
|
||||
/// <returns>The Message that was represented by the JObject.</returns>
|
||||
Message DeserializeMessage(JObject messageJson);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// 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;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes messages in the JSON RPC format. Used primarily
|
||||
/// for language servers.
|
||||
/// </summary>
|
||||
public class JsonRpcMessageSerializer : IMessageSerializer
|
||||
{
|
||||
public JObject SerializeMessage(Message message)
|
||||
{
|
||||
JObject messageObject = new JObject();
|
||||
|
||||
messageObject.Add("jsonrpc", JToken.FromObject("2.0"));
|
||||
|
||||
if (message.MessageType == MessageType.Request)
|
||||
{
|
||||
messageObject.Add("id", JToken.FromObject(message.Id));
|
||||
messageObject.Add("method", message.Method);
|
||||
messageObject.Add("params", message.Contents);
|
||||
}
|
||||
else if (message.MessageType == MessageType.Event)
|
||||
{
|
||||
messageObject.Add("method", message.Method);
|
||||
messageObject.Add("params", message.Contents);
|
||||
}
|
||||
else if (message.MessageType == MessageType.Response)
|
||||
{
|
||||
messageObject.Add("id", JToken.FromObject(message.Id));
|
||||
|
||||
if (message.Error != null)
|
||||
{
|
||||
// Write error
|
||||
messageObject.Add("error", message.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write result
|
||||
messageObject.Add("result", message.Contents);
|
||||
}
|
||||
}
|
||||
|
||||
return messageObject;
|
||||
}
|
||||
|
||||
public Message DeserializeMessage(JObject messageJson)
|
||||
{
|
||||
// TODO: Check for jsonrpc version
|
||||
|
||||
JToken token = null;
|
||||
if (messageJson.TryGetValue("id", out token))
|
||||
{
|
||||
// Message is a Request or Response
|
||||
string messageId = token.ToString();
|
||||
|
||||
if (messageJson.TryGetValue("result", out token))
|
||||
{
|
||||
return Message.Response(messageId, null, token);
|
||||
}
|
||||
else if (messageJson.TryGetValue("error", out token))
|
||||
{
|
||||
return Message.ResponseError(messageId, null, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
JToken messageParams = null;
|
||||
messageJson.TryGetValue("params", out messageParams);
|
||||
|
||||
if (!messageJson.TryGetValue("method", out token))
|
||||
{
|
||||
// TODO: Throw parse error
|
||||
}
|
||||
|
||||
return Message.Request(messageId, token.ToString(), messageParams);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Messages without an id are events
|
||||
JToken messageParams = token;
|
||||
messageJson.TryGetValue("params", out messageParams);
|
||||
|
||||
if (!messageJson.TryGetValue("method", out token))
|
||||
{
|
||||
// TODO: Throw parse error
|
||||
}
|
||||
|
||||
return Message.Event(token.ToString(), messageParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes messages in the V8 format. Used primarily for debug adapters.
|
||||
/// </summary>
|
||||
public class V8MessageSerializer : IMessageSerializer
|
||||
{
|
||||
public JObject SerializeMessage(Message message)
|
||||
{
|
||||
JObject messageObject = new JObject();
|
||||
|
||||
if (message.MessageType == MessageType.Request)
|
||||
{
|
||||
messageObject.Add("type", JToken.FromObject("request"));
|
||||
messageObject.Add("seq", JToken.FromObject(message.Id));
|
||||
messageObject.Add("command", message.Method);
|
||||
messageObject.Add("arguments", message.Contents);
|
||||
}
|
||||
else if (message.MessageType == MessageType.Event)
|
||||
{
|
||||
messageObject.Add("type", JToken.FromObject("event"));
|
||||
messageObject.Add("event", message.Method);
|
||||
messageObject.Add("body", message.Contents);
|
||||
}
|
||||
else if (message.MessageType == MessageType.Response)
|
||||
{
|
||||
messageObject.Add("type", JToken.FromObject("response"));
|
||||
messageObject.Add("request_seq", JToken.FromObject(message.Id));
|
||||
messageObject.Add("command", message.Method);
|
||||
|
||||
if (message.Error != null)
|
||||
{
|
||||
// Write error
|
||||
messageObject.Add("success", JToken.FromObject(false));
|
||||
messageObject.Add("message", message.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write result
|
||||
messageObject.Add("success", JToken.FromObject(true));
|
||||
messageObject.Add("body", message.Contents);
|
||||
}
|
||||
}
|
||||
|
||||
return messageObject;
|
||||
}
|
||||
|
||||
public Message DeserializeMessage(JObject messageJson)
|
||||
{
|
||||
JToken token = null;
|
||||
|
||||
if (messageJson.TryGetValue("type", out token))
|
||||
{
|
||||
string messageType = token.ToString();
|
||||
|
||||
if (string.Equals("request", messageType, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return Message.Request(
|
||||
messageJson.GetValue("seq").ToString(),
|
||||
messageJson.GetValue("command").ToString(),
|
||||
messageJson.GetValue("arguments"));
|
||||
}
|
||||
else if (string.Equals("response", messageType, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
if (messageJson.TryGetValue("success", out token))
|
||||
{
|
||||
// Was the response for a successful request?
|
||||
if (token.ToObject<bool>() == true)
|
||||
{
|
||||
return Message.Response(
|
||||
messageJson.GetValue("request_seq").ToString(),
|
||||
messageJson.GetValue("command").ToString(),
|
||||
messageJson.GetValue("body"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Message.ResponseError(
|
||||
messageJson.GetValue("request_seq").ToString(),
|
||||
messageJson.GetValue("command").ToString(),
|
||||
messageJson.GetValue("message"));
|
||||
}
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// // TODO: Parse error
|
||||
// }
|
||||
|
||||
}
|
||||
else if (string.Equals("event", messageType, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return Message.Event(
|
||||
messageJson.GetValue("event").ToString(),
|
||||
messageJson.GetValue("body"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Message.Unknown();
|
||||
}
|
||||
}
|
||||
|
||||
return Message.Unknown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
185
src/Microsoft.SqlTools.Hosting/Hosting/ServiceHost.cs
Normal file
185
src/Microsoft.SqlTools.Hosting/Hosting/ServiceHost.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
//
|
||||
// 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.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting
|
||||
{
|
||||
/// <summary>
|
||||
/// SQL Tools VS Code Language Server request handler. Provides the entire JSON RPC
|
||||
/// implementation for sending/receiving JSON requests and dispatching the requests to
|
||||
/// handlers that are registered prior to startup.
|
||||
/// </summary>
|
||||
public sealed class ServiceHost : ServiceHostBase
|
||||
{
|
||||
/// <summary>
|
||||
/// This timeout limits the amount of time that shutdown tasks can take to complete
|
||||
/// prior to the process shutting down.
|
||||
/// </summary>
|
||||
private const int ShutdownTimeoutInSeconds = 120;
|
||||
public static readonly string[] CompletionTriggerCharacters = new string[] { ".", "-", ":", "\\", "[", "\"" };
|
||||
|
||||
#region Singleton Instance Code
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of the service host for internal storage
|
||||
/// </summary>
|
||||
private static readonly Lazy<ServiceHost> instance = new Lazy<ServiceHost>(() => new ServiceHost());
|
||||
|
||||
/// <summary>
|
||||
/// Current instance of the ServiceHost
|
||||
/// </summary>
|
||||
public static ServiceHost Instance
|
||||
{
|
||||
get { return instance.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs new instance of ServiceHost using the host and profile details provided.
|
||||
/// Access is private to ensure only one instance exists at a time.
|
||||
/// </summary>
|
||||
private ServiceHost() : base(new StdioServerChannel())
|
||||
{
|
||||
// Initialize the shutdown activities
|
||||
shutdownCallbacks = new List<ShutdownCallback>();
|
||||
initializeCallbacks = new List<InitializeCallback>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide initialization that must occur after the service host is started
|
||||
/// </summary>
|
||||
public void InitializeRequestHandlers()
|
||||
{
|
||||
// Register the requests that this service host will handle
|
||||
this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
|
||||
this.SetRequestHandler(ShutdownRequest.Type, this.HandleShutdownRequest);
|
||||
this.SetRequestHandler(VersionRequest.Type, HandleVersionRequest);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Member Variables
|
||||
|
||||
/// <summary>
|
||||
/// Delegate definition for the host shutdown event
|
||||
/// </summary>
|
||||
/// <param name="shutdownParams"></param>
|
||||
/// <param name="shutdownRequestContext"></param>
|
||||
public delegate Task ShutdownCallback(object shutdownParams, RequestContext<object> shutdownRequestContext);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate definition for the host initialization event
|
||||
/// </summary>
|
||||
/// <param name="startupParams"></param>
|
||||
/// <param name="requestContext"></param>
|
||||
public delegate Task InitializeCallback(InitializeRequest startupParams, RequestContext<InitializeResult> requestContext);
|
||||
|
||||
private readonly List<ShutdownCallback> shutdownCallbacks;
|
||||
|
||||
private readonly List<InitializeCallback> initializeCallbacks;
|
||||
|
||||
private static readonly Version serviceVersion = Assembly.GetEntryAssembly().GetName().Version;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new callback to be called when the shutdown request is submitted
|
||||
/// </summary>
|
||||
/// <param name="callback">Callback to perform when a shutdown request is submitted</param>
|
||||
public void RegisterShutdownTask(ShutdownCallback callback)
|
||||
{
|
||||
shutdownCallbacks.Add(callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new method to be called when the initialize request is submitted
|
||||
/// </summary>
|
||||
/// <param name="callback">Callback to perform when an initialize request is submitted</param>
|
||||
public void RegisterInitializeTask(InitializeCallback callback)
|
||||
{
|
||||
initializeCallbacks.Add(callback);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Request Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handles the shutdown event for the Language Server
|
||||
/// </summary>
|
||||
private async Task HandleShutdownRequest(object shutdownParams, RequestContext<object> requestContext)
|
||||
{
|
||||
Logger.Write(LogLevel.Normal, "Service host is shutting down...");
|
||||
|
||||
// Call all the shutdown methods provided by the service components
|
||||
Task[] shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext)).ToArray();
|
||||
TimeSpan shutdownTimeout = TimeSpan.FromSeconds(ShutdownTimeoutInSeconds);
|
||||
// shut down once all tasks are completed, or after the timeout expires, whichever comes first.
|
||||
await Task.WhenAny(Task.WhenAll(shutdownTasks), Task.Delay(shutdownTimeout)).ContinueWith(t => Environment.Exit(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the initialization request
|
||||
/// </summary>
|
||||
/// <param name="initializeParams"></param>
|
||||
/// <param name="requestContext"></param>
|
||||
/// <returns></returns>
|
||||
internal async Task HandleInitializeRequest(InitializeRequest initializeParams, RequestContext<InitializeResult> requestContext)
|
||||
{
|
||||
// Call all tasks that registered on the initialize request
|
||||
var initializeTasks = initializeCallbacks.Select(t => t(initializeParams, requestContext));
|
||||
await Task.WhenAll(initializeTasks);
|
||||
|
||||
// TODO: Figure out where this needs to go to be agnostic of the language
|
||||
|
||||
// Send back what this server can do
|
||||
await requestContext.SendResult(
|
||||
new InitializeResult
|
||||
{
|
||||
Capabilities = new ServerCapabilities
|
||||
{
|
||||
TextDocumentSync = TextDocumentSyncKind.Incremental,
|
||||
DefinitionProvider = true,
|
||||
ReferencesProvider = false,
|
||||
DocumentFormattingProvider = true,
|
||||
DocumentRangeFormattingProvider = true,
|
||||
DocumentHighlightProvider = false,
|
||||
HoverProvider = true,
|
||||
CompletionProvider = new CompletionOptions
|
||||
{
|
||||
ResolveProvider = true,
|
||||
TriggerCharacters = CompletionTriggerCharacters
|
||||
},
|
||||
SignatureHelpProvider = new SignatureHelpOptions
|
||||
{
|
||||
TriggerCharacters = new string[] { " ", "," }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the version request. Sends back the server version as result.
|
||||
/// </summary>
|
||||
private static async Task HandleVersionRequest(
|
||||
object versionRequestParams,
|
||||
RequestContext<string> requestContext)
|
||||
{
|
||||
await requestContext.SendResult(serviceVersion.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
47
src/Microsoft.SqlTools.Hosting/Hosting/ServiceHostBase.cs
Normal file
47
src/Microsoft.SqlTools.Hosting/Hosting/ServiceHostBase.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Hosting
|
||||
{
|
||||
public abstract class ServiceHostBase : ProtocolEndpoint
|
||||
{
|
||||
private bool isStarted;
|
||||
private TaskCompletionSource<bool> serverExitedTask;
|
||||
|
||||
protected ServiceHostBase(ChannelBase serverChannel) :
|
||||
base(serverChannel, MessageProtocolType.LanguageServer)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task OnStart()
|
||||
{
|
||||
// Register handlers for server lifetime messages
|
||||
|
||||
this.SetEventHandler(ExitNotification.Type, this.HandleExitNotification);
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
private async Task HandleExitNotification(
|
||||
object exitParams,
|
||||
EventContext eventContext)
|
||||
{
|
||||
// Stop the server channel
|
||||
await this.Stop();
|
||||
|
||||
// Notify any waiter that the server has exited
|
||||
if (this.serverExitedTask != null)
|
||||
{
|
||||
this.serverExitedTask.SetResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#if false
|
||||
using Microsoft.SqlTools.EditorServices.Extensions;
|
||||
using Microsoft.SqlTools.EditorServices.Protocol.LanguageServer;
|
||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.EditorServices.Protocol.Server
|
||||
{
|
||||
internal class LanguageServerEditorOperations : IEditorOperations
|
||||
{
|
||||
private EditorSession editorSession;
|
||||
private IMessageSender messageSender;
|
||||
|
||||
public LanguageServerEditorOperations(
|
||||
EditorSession editorSession,
|
||||
IMessageSender messageSender)
|
||||
{
|
||||
this.editorSession = editorSession;
|
||||
this.messageSender = messageSender;
|
||||
}
|
||||
|
||||
public async Task<EditorContext> GetEditorContext()
|
||||
{
|
||||
ClientEditorContext clientContext =
|
||||
await this.messageSender.SendRequest(
|
||||
GetEditorContextRequest.Type,
|
||||
new GetEditorContextRequest(),
|
||||
true);
|
||||
|
||||
return this.ConvertClientEditorContext(clientContext);
|
||||
}
|
||||
|
||||
public async Task InsertText(string filePath, string text, BufferRange insertRange)
|
||||
{
|
||||
await this.messageSender.SendRequest(
|
||||
InsertTextRequest.Type,
|
||||
new InsertTextRequest
|
||||
{
|
||||
FilePath = filePath,
|
||||
InsertText = text,
|
||||
InsertRange =
|
||||
new Range
|
||||
{
|
||||
Start = new Position
|
||||
{
|
||||
Line = insertRange.Start.Line - 1,
|
||||
Character = insertRange.Start.Column - 1
|
||||
},
|
||||
End = new Position
|
||||
{
|
||||
Line = insertRange.End.Line - 1,
|
||||
Character = insertRange.End.Column - 1
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
|
||||
// TODO: Set the last param back to true!
|
||||
}
|
||||
|
||||
public Task SetSelection(BufferRange selectionRange)
|
||||
{
|
||||
return this.messageSender.SendRequest(
|
||||
SetSelectionRequest.Type,
|
||||
new SetSelectionRequest
|
||||
{
|
||||
SelectionRange =
|
||||
new Range
|
||||
{
|
||||
Start = new Position
|
||||
{
|
||||
Line = selectionRange.Start.Line - 1,
|
||||
Character = selectionRange.Start.Column - 1
|
||||
},
|
||||
End = new Position
|
||||
{
|
||||
Line = selectionRange.End.Line - 1,
|
||||
Character = selectionRange.End.Column - 1
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public EditorContext ConvertClientEditorContext(
|
||||
ClientEditorContext clientContext)
|
||||
{
|
||||
return
|
||||
new EditorContext(
|
||||
this,
|
||||
this.editorSession.Workspace.GetFile(clientContext.CurrentFilePath),
|
||||
new BufferPosition(
|
||||
clientContext.CursorPosition.Line + 1,
|
||||
clientContext.CursorPosition.Character + 1),
|
||||
new BufferRange(
|
||||
clientContext.SelectionRange.Start.Line + 1,
|
||||
clientContext.SelectionRange.Start.Character + 1,
|
||||
clientContext.SelectionRange.End.Line + 1,
|
||||
clientContext.SelectionRange.End.Character + 1));
|
||||
}
|
||||
|
||||
public Task OpenFile(string filePath)
|
||||
{
|
||||
return
|
||||
this.messageSender.SendRequest(
|
||||
OpenFileRequest.Type,
|
||||
filePath,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
953
src/Microsoft.SqlTools.Hosting/Localization/sr.Designer.cs
generated
Normal file
953
src/Microsoft.SqlTools.Hosting/Localization/sr.Designer.cs
generated
Normal file
@@ -0,0 +1,953 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.SqlTools.Hosting.Localization {
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class sr {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
internal sr() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.SqlTools.ServiceLayer.Localization.sr", typeof(sr).GetTypeInfo().Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to File '{0}' recursively included..
|
||||
/// </summary>
|
||||
public static string BatchParser_CircularReference {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParser_CircularReference", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Missing end comment mark '*/'..
|
||||
/// </summary>
|
||||
public static string BatchParser_CommentNotTerminated {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParser_CommentNotTerminated", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Incorrect syntax was encountered while parsing '{0}'..
|
||||
/// </summary>
|
||||
public static string BatchParser_IncorrectSyntax {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParser_IncorrectSyntax", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unclosed quotation mark after the character string..
|
||||
/// </summary>
|
||||
public static string BatchParser_StringNotTerminated {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParser_StringNotTerminated", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Variable {0} is not defined..
|
||||
/// </summary>
|
||||
public static string BatchParser_VariableNotDefined {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParser_VariableNotDefined", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Canceling batch parser wrapper batch execution..
|
||||
/// </summary>
|
||||
public static string BatchParserWrapperExecutionEngineBatchCancelling {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchCancelling", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Batch parser wrapper execution engine batch message received: Message: {0} Detailed message: {1}.
|
||||
/// </summary>
|
||||
public static string BatchParserWrapperExecutionEngineBatchMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Batch parser wrapper execution engine batch ResultSet finished..
|
||||
/// </summary>
|
||||
public static string BatchParserWrapperExecutionEngineBatchResultSetFinished {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchResultSetFinished", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Batch parser wrapper execution engine batch ResultSet processing: DataReader.FieldCount: {0} DataReader.RecordsAffected: {1}.
|
||||
/// </summary>
|
||||
public static string BatchParserWrapperExecutionEngineBatchResultSetProcessing {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchResultSetProcessing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to SQL Execution error: {0}.
|
||||
/// </summary>
|
||||
public static string BatchParserWrapperExecutionEngineError {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParserWrapperExecutionEngineError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Batch parser wrapper execution: {0} found... at line {1}: {2} Description: {3}.
|
||||
/// </summary>
|
||||
public static string BatchParserWrapperExecutionError {
|
||||
get {
|
||||
return ResourceManager.GetString("BatchParserWrapperExecutionError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connection details object cannot be null.
|
||||
/// </summary>
|
||||
public static string ConnectionParamsValidateNullConnection {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionParamsValidateNullConnection", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OwnerUri cannot be null or empty.
|
||||
/// </summary>
|
||||
public static string ConnectionParamsValidateNullOwnerUri {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionParamsValidateNullOwnerUri", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ServerName cannot be null or empty.
|
||||
/// </summary>
|
||||
public static string ConnectionParamsValidateNullServerName {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionParamsValidateNullServerName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} cannot be null or empty when using SqlLogin authentication.
|
||||
/// </summary>
|
||||
public static string ConnectionParamsValidateNullSqlAuth {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionParamsValidateNullSqlAuth", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connection parameters cannot be null.
|
||||
/// </summary>
|
||||
public static string ConnectionServiceConnectErrorNullParams {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionServiceConnectErrorNullParams", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connection canceled.
|
||||
/// </summary>
|
||||
public static string ConnectionServiceConnectionCanceled {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionServiceConnectionCanceled", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid value '{0}' for AuthenticationType. Valid values are 'Integrated' and 'SqlLogin'..
|
||||
/// </summary>
|
||||
public static string ConnectionServiceConnStringInvalidAuthType {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionServiceConnStringInvalidAuthType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid value '{0}' for ApplicationIntent. Valid values are 'ReadWrite' and 'ReadOnly'..
|
||||
/// </summary>
|
||||
public static string ConnectionServiceConnStringInvalidIntent {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionServiceConnStringInvalidIntent", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to SpecifiedUri '{0}' does not have existing connection.
|
||||
/// </summary>
|
||||
public static string ConnectionServiceListDbErrorNotConnected {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionServiceListDbErrorNotConnected", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OwnerUri cannot be null or empty.
|
||||
/// </summary>
|
||||
public static string ConnectionServiceListDbErrorNullOwnerUri {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionServiceListDbErrorNullOwnerUri", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Win32Credential object is already disposed.
|
||||
/// </summary>
|
||||
public static string CredentialServiceWin32CredentialDisposed {
|
||||
get {
|
||||
return ResourceManager.GetString("CredentialServiceWin32CredentialDisposed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid CriticalHandle!.
|
||||
/// </summary>
|
||||
public static string CredentialsServiceInvalidCriticalHandle {
|
||||
get {
|
||||
return ResourceManager.GetString("CredentialsServiceInvalidCriticalHandle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The password has exceeded 512 bytes.
|
||||
/// </summary>
|
||||
public static string CredentialsServicePasswordLengthExceeded {
|
||||
get {
|
||||
return ResourceManager.GetString("CredentialsServicePasswordLengthExceeded", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Target must be specified to delete a credential.
|
||||
/// </summary>
|
||||
public static string CredentialsServiceTargetForDelete {
|
||||
get {
|
||||
return ResourceManager.GetString("CredentialsServiceTargetForDelete", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Target must be specified to check existance of a credential.
|
||||
/// </summary>
|
||||
public static string CredentialsServiceTargetForLookup {
|
||||
get {
|
||||
return ResourceManager.GetString("CredentialsServiceTargetForLookup", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An error occurred while the batch was being processed. The error message is: {0}.
|
||||
/// </summary>
|
||||
public static string EE_BatchError_Exception {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_BatchError_Exception", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An error occurred while the batch was being executed..
|
||||
/// </summary>
|
||||
public static string EE_BatchExecutionError_Halting {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_BatchExecutionError_Halting", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An error occurred while the batch was being executed, but the error has been ignored..
|
||||
/// </summary>
|
||||
public static string EE_BatchExecutionError_Ignoring {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_BatchExecutionError_Ignoring", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ({0} row(s) affected).
|
||||
/// </summary>
|
||||
public static string EE_BatchExecutionInfo_RowsAffected {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_BatchExecutionInfo_RowsAffected", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Msg {0}, Level {1}, State {2}.
|
||||
/// </summary>
|
||||
public static string EE_BatchSqlMessageNoLineInfo {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_BatchSqlMessageNoLineInfo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Msg {0}, Level {1}, State {2}, Line {3}.
|
||||
/// </summary>
|
||||
public static string EE_BatchSqlMessageNoProcedureInfo {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_BatchSqlMessageNoProcedureInfo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Msg {0}, Level {1}, State {2}, Procedure {3}, Line {4}.
|
||||
/// </summary>
|
||||
public static string EE_BatchSqlMessageWithProcedureInfo {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_BatchSqlMessageWithProcedureInfo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Command {0} is not supported..
|
||||
/// </summary>
|
||||
public static string EE_ExecutionError_CommandNotSupported {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_ExecutionError_CommandNotSupported", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The variable {0} could not be found..
|
||||
/// </summary>
|
||||
public static string EE_ExecutionError_VariableNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_ExecutionError_VariableNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Execution completed {0} times....
|
||||
/// </summary>
|
||||
public static string EE_ExecutionInfo_FinalizingLoop {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_ExecutionInfo_FinalizingLoop", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Starting execution loop of {0} times....
|
||||
/// </summary>
|
||||
public static string EE_ExecutionInfo_InitilizingLoop {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_ExecutionInfo_InitilizingLoop", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You cancelled the query..
|
||||
/// </summary>
|
||||
public static string EE_ExecutionInfo_QueryCancelledbyUser {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_ExecutionInfo_QueryCancelledbyUser", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The previous execution is not yet complete..
|
||||
/// </summary>
|
||||
public static string EE_ExecutionNotYetCompleteError {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_ExecutionNotYetCompleteError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A scripting error occurred..
|
||||
/// </summary>
|
||||
public static string EE_ScriptError_Error {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_ScriptError_Error", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A fatal error occurred..
|
||||
/// </summary>
|
||||
public static string EE_ScriptError_FatalError {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_ScriptError_FatalError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Incorrect syntax was encountered while {0} was being parsed..
|
||||
/// </summary>
|
||||
public static string EE_ScriptError_ParsingSyntax {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_ScriptError_ParsingSyntax", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Scripting warning..
|
||||
/// </summary>
|
||||
public static string EE_ScriptError_Warning {
|
||||
get {
|
||||
return ResourceManager.GetString("EE_ScriptError_Warning", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Replacement of an empty string by an empty string..
|
||||
/// </summary>
|
||||
public static string ErrorEmptyStringReplacement {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorEmptyStringReplacement", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot convert SqlCodeObject Type {0} to Type {1}.
|
||||
/// </summary>
|
||||
public static string ErrorUnexpectedCodeObjectType {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorUnexpectedCodeObjectType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Message header must separate key and value using ':'.
|
||||
/// </summary>
|
||||
public static string HostingHeaderMissingColon {
|
||||
get {
|
||||
return ResourceManager.GetString("HostingHeaderMissingColon", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fatal error: Content-Length header must be provided.
|
||||
/// </summary>
|
||||
public static string HostingHeaderMissingContentLengthHeader {
|
||||
get {
|
||||
return ResourceManager.GetString("HostingHeaderMissingContentLengthHeader", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fatal error: Content-Length value is not an integer.
|
||||
/// </summary>
|
||||
public static string HostingHeaderMissingContentLengthValue {
|
||||
get {
|
||||
return ResourceManager.GetString("HostingHeaderMissingContentLengthValue", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to MessageReader's input stream ended unexpectedly, terminating.
|
||||
/// </summary>
|
||||
public static string HostingUnexpectedEndOfStream {
|
||||
get {
|
||||
return ResourceManager.GetString("HostingUnexpectedEndOfStream", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Service of type {0} cannot be created by ExtensionLoader<{1}>.
|
||||
/// </summary>
|
||||
public static string IncompatibleServiceForExtensionLoader {
|
||||
get {
|
||||
return ResourceManager.GetString("IncompatibleServiceForExtensionLoader", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Multiple services found for type {0}, expected only 1.
|
||||
/// </summary>
|
||||
public static string MultipleServicesFound {
|
||||
get {
|
||||
return ResourceManager.GetString("MultipleServicesFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This feature is currently not supported on Azure SQL DB and Data Warehouse: {0}.
|
||||
/// </summary>
|
||||
public static string PeekDefinitionAzureError {
|
||||
get {
|
||||
return ResourceManager.GetString("PeekDefinitionAzureError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No database object was retrieved..
|
||||
/// </summary>
|
||||
public static string PeekDefinitionDatabaseError {
|
||||
get {
|
||||
return ResourceManager.GetString("PeekDefinitionDatabaseError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An unexpected error occurred during Peek Definition execution: {0}.
|
||||
/// </summary>
|
||||
public static string PeekDefinitionError {
|
||||
get {
|
||||
return ResourceManager.GetString("PeekDefinitionError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No results were found..
|
||||
/// </summary>
|
||||
public static string PeekDefinitionNoResultsError {
|
||||
get {
|
||||
return ResourceManager.GetString("PeekDefinitionNoResultsError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please connect to a server..
|
||||
/// </summary>
|
||||
public static string PeekDefinitionNotConnectedError {
|
||||
get {
|
||||
return ResourceManager.GetString("PeekDefinitionNotConnectedError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Operation timed out..
|
||||
/// </summary>
|
||||
public static string PeekDefinitionTimedoutError {
|
||||
get {
|
||||
return ResourceManager.GetString("PeekDefinitionTimedoutError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This object type is currently not supported by this feature..
|
||||
/// </summary>
|
||||
public static string PeekDefinitionTypeNotSupportedError {
|
||||
get {
|
||||
return ResourceManager.GetString("PeekDefinitionTypeNotSupportedError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to (1 row affected).
|
||||
/// </summary>
|
||||
public static string QueryServiceAffectedOneRow {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceAffectedOneRow", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ({0} rows affected).
|
||||
/// </summary>
|
||||
public static string QueryServiceAffectedRows {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceAffectedRows", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The query has already completed, it cannot be cancelled.
|
||||
/// </summary>
|
||||
public static string QueryServiceCancelAlreadyCompleted {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceCancelAlreadyCompleted", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Query successfully cancelled, failed to dispose query. Owner URI not found..
|
||||
/// </summary>
|
||||
public static string QueryServiceCancelDisposeFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceCancelDisposeFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to (No column name).
|
||||
/// </summary>
|
||||
public static string QueryServiceColumnNull {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceColumnNull", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Commands completed successfully..
|
||||
/// </summary>
|
||||
public static string QueryServiceCompletedSuccessfully {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceCompletedSuccessfully", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Maximum number of bytes to return must be greater than zero.
|
||||
/// </summary>
|
||||
public static string QueryServiceDataReaderByteCountInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceDataReaderByteCountInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Maximum number of chars to return must be greater than zero.
|
||||
/// </summary>
|
||||
public static string QueryServiceDataReaderCharCountInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceDataReaderCharCountInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Maximum number of XML bytes to return must be greater than zero.
|
||||
/// </summary>
|
||||
public static string QueryServiceDataReaderXmlCountInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceDataReaderXmlCountInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Msg {0}, Level {1}, State {2}, Line {3}{4}{5}.
|
||||
/// </summary>
|
||||
public static string QueryServiceErrorFormat {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceErrorFormat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Could not retrieve an execution plan from the result set .
|
||||
/// </summary>
|
||||
public static string QueryServiceExecutionPlanNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceExecutionPlanNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to FileStreamWrapper must be initialized before performing operations.
|
||||
/// </summary>
|
||||
public static string QueryServiceFileWrapperNotInitialized {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceFileWrapperNotInitialized", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This FileStreamWrapper cannot be used for writing.
|
||||
/// </summary>
|
||||
public static string QueryServiceFileWrapperReadOnly {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceFileWrapperReadOnly", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Access method cannot be write-only.
|
||||
/// </summary>
|
||||
public static string QueryServiceFileWrapperWriteOnly {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceFileWrapperWriteOnly", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sender for OnInfoMessage event must be a SqlConnection.
|
||||
/// </summary>
|
||||
public static string QueryServiceMessageSenderNotSql {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceMessageSenderNotSql", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Query was canceled by user.
|
||||
/// </summary>
|
||||
public static string QueryServiceQueryCancelled {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceQueryCancelled", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Query failed: {0}.
|
||||
/// </summary>
|
||||
public static string QueryServiceQueryFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceQueryFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A query is already in progress for this editor session. Please cancel this query or wait for its completion..
|
||||
/// </summary>
|
||||
public static string QueryServiceQueryInProgress {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceQueryInProgress", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This editor is not connected to a database.
|
||||
/// </summary>
|
||||
public static string QueryServiceQueryInvalidOwnerUri {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceQueryInvalidOwnerUri", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The requested query does not exist.
|
||||
/// </summary>
|
||||
public static string QueryServiceRequestsNoQuery {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceRequestsNoQuery", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Could not retrieve column schema for result set.
|
||||
/// </summary>
|
||||
public static string QueryServiceResultSetNoColumnSchema {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceResultSetNoColumnSchema", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot read subset unless the results have been read from the server.
|
||||
/// </summary>
|
||||
public static string QueryServiceResultSetNotRead {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceResultSetNotRead", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reader cannot be null.
|
||||
/// </summary>
|
||||
public static string QueryServiceResultSetReaderNull {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceResultSetReaderNull", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Row count must be a positive integer.
|
||||
/// </summary>
|
||||
public static string QueryServiceResultSetRowCountOutOfRange {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceResultSetRowCountOutOfRange", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Start row cannot be less than 0 or greater than the number of rows in the result set.
|
||||
/// </summary>
|
||||
public static string QueryServiceResultSetStartRowOutOfRange {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceResultSetStartRowOutOfRange", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Failed to save {0}: {1}.
|
||||
/// </summary>
|
||||
public static string QueryServiceSaveAsFail {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceSaveAsFail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A save request to the same path is in progress.
|
||||
/// </summary>
|
||||
public static string QueryServiceSaveAsInProgress {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceSaveAsInProgress", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Internal error occurred while starting save task.
|
||||
/// </summary>
|
||||
public static string QueryServiceSaveAsMiscStartingError {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceSaveAsMiscStartingError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Result cannot be saved until query execution has completed.
|
||||
/// </summary>
|
||||
public static string QueryServiceSaveAsResultSetNotComplete {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceSaveAsResultSetNotComplete", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The batch has not completed, yet.
|
||||
/// </summary>
|
||||
public static string QueryServiceSubsetBatchNotCompleted {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceSubsetBatchNotCompleted", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Batch index cannot be less than 0 or greater than the number of batches.
|
||||
/// </summary>
|
||||
public static string QueryServiceSubsetBatchOutOfRange {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceSubsetBatchOutOfRange", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Result set index cannot be less than 0 or greater than the number of result sets.
|
||||
/// </summary>
|
||||
public static string QueryServiceSubsetResultSetOutOfRange {
|
||||
get {
|
||||
return ResourceManager.GetString("QueryServiceSubsetResultSetOutOfRange", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot register service for type {0}, one or more services already registered.
|
||||
/// </summary>
|
||||
public static string ServiceAlreadyRegistered {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceAlreadyRegistered", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Service {0} was not found in the service provider.
|
||||
/// </summary>
|
||||
public static string ServiceNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Service of Type {0} is not compatible with registered Type {1}.
|
||||
/// </summary>
|
||||
public static string ServiceNotOfExpectedType {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceNotOfExpectedType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to SetServiceProvider() was not called to establish the required service provider.
|
||||
/// </summary>
|
||||
public static string ServiceProviderNotSet {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceProviderNotSet", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to EN_LOCALIZATION.
|
||||
/// </summary>
|
||||
public static string TestLocalizationConstant {
|
||||
get {
|
||||
return ResourceManager.GetString("TestLocalizationConstant", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to For more information about this error, see the troubleshooting topics in the product documentation..
|
||||
/// </summary>
|
||||
public static string TroubleshootingAssistanceMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("TroubleshootingAssistanceMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Start position ({0}, {1}) must come before or be equal to the end position ({2}, {3}).
|
||||
/// </summary>
|
||||
public static string WorkspaceServiceBufferPositionOutOfOrder {
|
||||
get {
|
||||
return ResourceManager.GetString("WorkspaceServiceBufferPositionOutOfOrder", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Position is outside of column range for line {0}.
|
||||
/// </summary>
|
||||
public static string WorkspaceServicePositionColumnOutOfRange {
|
||||
get {
|
||||
return ResourceManager.GetString("WorkspaceServicePositionColumnOutOfRange", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Position is outside of file line range.
|
||||
/// </summary>
|
||||
public static string WorkspaceServicePositionLineOutOfRange {
|
||||
get {
|
||||
return ResourceManager.GetString("WorkspaceServicePositionLineOutOfRange", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
227
src/Microsoft.SqlTools.Hosting/Localization/sr.cs
Executable file
227
src/Microsoft.SqlTools.Hosting/Localization/sr.cs
Executable file
@@ -0,0 +1,227 @@
|
||||
// WARNING:
|
||||
// This file was generated by the Microsoft DataWarehouse String Resource Tool 1.37.0.0
|
||||
// from information in sr.strings
|
||||
// DO NOT MODIFY THIS FILE'S CONTENTS, THEY WILL BE OVERWRITTEN
|
||||
//
|
||||
namespace Microsoft.SqlTools.Hosting
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Globalization;
|
||||
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SR
|
||||
{
|
||||
protected SR()
|
||||
{ }
|
||||
|
||||
public static CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.Culture;
|
||||
}
|
||||
set
|
||||
{
|
||||
Keys.Culture = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string CredentialsServiceInvalidCriticalHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.CredentialsServiceInvalidCriticalHandle);
|
||||
}
|
||||
}
|
||||
|
||||
public static string CredentialsServicePasswordLengthExceeded
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.CredentialsServicePasswordLengthExceeded);
|
||||
}
|
||||
}
|
||||
|
||||
public static string CredentialsServiceTargetForDelete
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.CredentialsServiceTargetForDelete);
|
||||
}
|
||||
}
|
||||
|
||||
public static string CredentialsServiceTargetForLookup
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.CredentialsServiceTargetForLookup);
|
||||
}
|
||||
}
|
||||
|
||||
public static string CredentialServiceWin32CredentialDisposed
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.CredentialServiceWin32CredentialDisposed);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ServiceAlreadyRegistered
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.ServiceAlreadyRegistered);
|
||||
}
|
||||
}
|
||||
|
||||
public static string MultipleServicesFound
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.MultipleServicesFound);
|
||||
}
|
||||
}
|
||||
|
||||
public static string IncompatibleServiceForExtensionLoader
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.IncompatibleServiceForExtensionLoader);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ServiceProviderNotSet
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.ServiceProviderNotSet);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ServiceNotFound
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.ServiceNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ServiceNotOfExpectedType
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.ServiceNotOfExpectedType);
|
||||
}
|
||||
}
|
||||
|
||||
public static string HostingUnexpectedEndOfStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.HostingUnexpectedEndOfStream);
|
||||
}
|
||||
}
|
||||
|
||||
public static string HostingHeaderMissingColon
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.HostingHeaderMissingColon);
|
||||
}
|
||||
}
|
||||
|
||||
public static string HostingHeaderMissingContentLengthHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.HostingHeaderMissingContentLengthHeader);
|
||||
}
|
||||
}
|
||||
|
||||
public static string HostingHeaderMissingContentLengthValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.HostingHeaderMissingContentLengthValue);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Keys
|
||||
{
|
||||
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.Hosting.Localization.SR", typeof(SR).GetTypeInfo().Assembly);
|
||||
|
||||
static CultureInfo _culture = null;
|
||||
|
||||
|
||||
public const string CredentialsServiceInvalidCriticalHandle = "CredentialsServiceInvalidCriticalHandle";
|
||||
|
||||
|
||||
public const string CredentialsServicePasswordLengthExceeded = "CredentialsServicePasswordLengthExceeded";
|
||||
|
||||
|
||||
public const string CredentialsServiceTargetForDelete = "CredentialsServiceTargetForDelete";
|
||||
|
||||
|
||||
public const string CredentialsServiceTargetForLookup = "CredentialsServiceTargetForLookup";
|
||||
|
||||
|
||||
public const string CredentialServiceWin32CredentialDisposed = "CredentialServiceWin32CredentialDisposed";
|
||||
|
||||
|
||||
public const string ServiceAlreadyRegistered = "ServiceAlreadyRegistered";
|
||||
|
||||
|
||||
public const string MultipleServicesFound = "MultipleServicesFound";
|
||||
|
||||
|
||||
public const string IncompatibleServiceForExtensionLoader = "IncompatibleServiceForExtensionLoader";
|
||||
|
||||
|
||||
public const string ServiceProviderNotSet = "ServiceProviderNotSet";
|
||||
|
||||
|
||||
public const string ServiceNotFound = "ServiceNotFound";
|
||||
|
||||
|
||||
public const string ServiceNotOfExpectedType = "ServiceNotOfExpectedType";
|
||||
|
||||
|
||||
public const string HostingUnexpectedEndOfStream = "HostingUnexpectedEndOfStream";
|
||||
|
||||
|
||||
public const string HostingHeaderMissingColon = "HostingHeaderMissingColon";
|
||||
|
||||
|
||||
public const string HostingHeaderMissingContentLengthHeader = "HostingHeaderMissingContentLengthHeader";
|
||||
|
||||
|
||||
public const string HostingHeaderMissingContentLengthValue = "HostingHeaderMissingContentLengthValue";
|
||||
|
||||
|
||||
private Keys()
|
||||
{ }
|
||||
|
||||
public static CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return _culture;
|
||||
}
|
||||
set
|
||||
{
|
||||
_culture = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetString(string key)
|
||||
{
|
||||
return resourceManager.GetString(key, _culture);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Microsoft.SqlTools.Hosting/Localization/sr.es.resx
Normal file
31
src/Microsoft.SqlTools.Hosting/Localization/sr.es.resx
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype"><value>text/microsoft-resx</value></resheader><resheader name="version"><value>1.3</value></resheader><resheader name="reader"><value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader><resheader name="writer"><value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader><data name="TestLocalizationConstant"><value>ES_LOCALIZATION</value></data>
|
||||
</root>
|
||||
180
src/Microsoft.SqlTools.Hosting/Localization/sr.resx
Executable file
180
src/Microsoft.SqlTools.Hosting/Localization/sr.resx
Executable file
@@ -0,0 +1,180 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype=">text/microsoft-resx</resheader>
|
||||
<resheader name="version=">2.0</resheader>
|
||||
<resheader name="reader=">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer=">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1="><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing=">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64=">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64=">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata=">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true=">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded=">
|
||||
<xsd:element name="metadata=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly=">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="CredentialsServiceInvalidCriticalHandle" xml:space="preserve">
|
||||
<value>Invalid CriticalHandle!</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="CredentialsServicePasswordLengthExceeded" xml:space="preserve">
|
||||
<value>The password has exceeded 512 bytes</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="CredentialsServiceTargetForDelete" xml:space="preserve">
|
||||
<value>Target must be specified to delete a credential</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="CredentialsServiceTargetForLookup" xml:space="preserve">
|
||||
<value>Target must be specified to check existance of a credential</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="CredentialServiceWin32CredentialDisposed" xml:space="preserve">
|
||||
<value>Win32Credential object is already disposed</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="ServiceAlreadyRegistered" xml:space="preserve">
|
||||
<value>Cannot register service for type {0}, one or more services already registered</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="MultipleServicesFound" xml:space="preserve">
|
||||
<value>Multiple services found for type {0}, expected only 1</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="IncompatibleServiceForExtensionLoader" xml:space="preserve">
|
||||
<value>Service of type {0} cannot be created by ExtensionLoader<{1}></value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="ServiceProviderNotSet" xml:space="preserve">
|
||||
<value>SetServiceProvider() was not called to establish the required service provider</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="ServiceNotFound" xml:space="preserve">
|
||||
<value>Service {0} was not found in the service provider</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="ServiceNotOfExpectedType" xml:space="preserve">
|
||||
<value>Service of Type {0} is not compatible with registered Type {1}</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="HostingUnexpectedEndOfStream" xml:space="preserve">
|
||||
<value>MessageReader's input stream ended unexpectedly, terminating</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="HostingHeaderMissingColon" xml:space="preserve">
|
||||
<value>Message header must separate key and value using ':'</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="HostingHeaderMissingContentLengthHeader" xml:space="preserve">
|
||||
<value>Fatal error: Content-Length header must be provided</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="HostingHeaderMissingContentLengthValue" xml:space="preserve">
|
||||
<value>Fatal error: Content-Length value is not an integer</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
</root>
|
||||
60
src/Microsoft.SqlTools.Hosting/Localization/sr.strings
Normal file
60
src/Microsoft.SqlTools.Hosting/Localization/sr.strings
Normal file
@@ -0,0 +1,60 @@
|
||||
# String resource file
|
||||
#
|
||||
# When processed by the String Resource Tool, this file generates
|
||||
# both a .CS and a .RESX file with the same name as the file.
|
||||
# The .CS file contains a class which can be used to access these
|
||||
# string resources, including the ability to format in
|
||||
# parameters, which are identified with the .NET {x} format
|
||||
# (see String.Format help).
|
||||
#
|
||||
# Comments below assume the file name is SR.strings.
|
||||
#
|
||||
# Lines starting with a semicolon ";" are also treated as comments, but
|
||||
# in a future version they will be extracted and made available in LocStudio
|
||||
# Put your comments to localizers _before_ the string they apply to.
|
||||
#
|
||||
# SMO build specific comment
|
||||
# after generating the .resx file, run srgen on it and get the .resx file
|
||||
# please remember to also check that .resx in, along with the
|
||||
# .strings and .cs files
|
||||
|
||||
[strings]
|
||||
|
||||
############################################################################
|
||||
# Credentials Service
|
||||
|
||||
CredentialsServiceInvalidCriticalHandle = Invalid CriticalHandle!
|
||||
|
||||
CredentialsServicePasswordLengthExceeded = The password has exceeded 512 bytes
|
||||
|
||||
CredentialsServiceTargetForDelete = Target must be specified to delete a credential
|
||||
|
||||
CredentialsServiceTargetForLookup = Target must be specified to check existance of a credential
|
||||
|
||||
CredentialServiceWin32CredentialDisposed = Win32Credential object is already disposed
|
||||
|
||||
############################################################################
|
||||
# Extensibility
|
||||
|
||||
ServiceAlreadyRegistered = Cannot register service for type {0}, one or more services already registered
|
||||
|
||||
MultipleServicesFound = Multiple services found for type {0}, expected only 1
|
||||
|
||||
IncompatibleServiceForExtensionLoader = Service of type {0} cannot be created by ExtensionLoader<{1}>
|
||||
|
||||
ServiceProviderNotSet = SetServiceProvider() was not called to establish the required service provider
|
||||
|
||||
ServiceNotFound = Service {0} was not found in the service provider
|
||||
|
||||
ServiceNotOfExpectedType = Service of Type {0} is not compatible with registered Type {1}
|
||||
|
||||
############################################################################
|
||||
# Hosting
|
||||
|
||||
HostingUnexpectedEndOfStream = MessageReader's input stream ended unexpectedly, terminating
|
||||
|
||||
HostingHeaderMissingColon = Message header must separate key and value using ':'
|
||||
|
||||
HostingHeaderMissingContentLengthHeader = Fatal error: Content-Length header must be provided
|
||||
|
||||
HostingHeaderMissingContentLengthValue = Fatal error: Content-Length value is not an integer
|
||||
82
src/Microsoft.SqlTools.Hosting/Localization/sr.xlf
Normal file
82
src/Microsoft.SqlTools.Hosting/Localization/sr.xlf
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" original="sr.resx" source-language="en">
|
||||
<body>
|
||||
<trans-unit id="CredentialsServiceInvalidCriticalHandle">
|
||||
<source>Invalid CriticalHandle!</source>
|
||||
<target state="new">Invalid CriticalHandle!</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="CredentialsServicePasswordLengthExceeded">
|
||||
<source>The password has exceeded 512 bytes</source>
|
||||
<target state="new">The password has exceeded 512 bytes</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="CredentialsServiceTargetForDelete">
|
||||
<source>Target must be specified to delete a credential</source>
|
||||
<target state="new">Target must be specified to delete a credential</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="CredentialsServiceTargetForLookup">
|
||||
<source>Target must be specified to check existance of a credential</source>
|
||||
<target state="new">Target must be specified to check existance of a credential</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="CredentialServiceWin32CredentialDisposed">
|
||||
<source>Win32Credential object is already disposed</source>
|
||||
<target state="new">Win32Credential object is already disposed</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HostingUnexpectedEndOfStream">
|
||||
<source>MessageReader's input stream ended unexpectedly, terminating</source>
|
||||
<target state="new">MessageReader's input stream ended unexpectedly, terminating</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HostingHeaderMissingColon">
|
||||
<source>Message header must separate key and value using ':'</source>
|
||||
<target state="new">Message header must separate key and value using ':'</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HostingHeaderMissingContentLengthHeader">
|
||||
<source>Fatal error: Content-Length header must be provided</source>
|
||||
<target state="new">Fatal error: Content-Length header must be provided</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="HostingHeaderMissingContentLengthValue">
|
||||
<source>Fatal error: Content-Length value is not an integer</source>
|
||||
<target state="new">Fatal error: Content-Length value is not an integer</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ServiceAlreadyRegistered">
|
||||
<source>Cannot register service for type {0}, one or more services already registered</source>
|
||||
<target state="new">Cannot register service for type {0}, one or more services already registered</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="MultipleServicesFound">
|
||||
<source>Multiple services found for type {0}, expected only 1</source>
|
||||
<target state="new">Multiple services found for type {0}, expected only 1</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="IncompatibleServiceForExtensionLoader">
|
||||
<source>Service of type {0} cannot be created by ExtensionLoader<{1}></source>
|
||||
<target state="new">Service of type {0} cannot be created by ExtensionLoader<{1}></target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ServiceProviderNotSet">
|
||||
<source>SetServiceProvider() was not called to establish the required service provider</source>
|
||||
<target state="new">SetServiceProvider() was not called to establish the required service provider</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ServiceNotFound">
|
||||
<source>Service {0} was not found in the service provider</source>
|
||||
<target state="new">Service {0} was not found in the service provider</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ServiceNotOfExpectedType">
|
||||
<source>Service of Type {0} is not compatible with registered Type {1}</source>
|
||||
<target state="new">Service of Type {0} is not compatible with registered Type {1}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" original="sr.es.resx" source-language="en" target-language="es">
|
||||
<body>
|
||||
<trans-unit id="TestLocalizationConstant">
|
||||
<source>ES_LOCALIZATION</source>
|
||||
<target state="new">ES_LOCALIZATION</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{2D61DC2B-DA66-441D-B9D0-919198F780F9}</ProjectGuid>
|
||||
<RootNamespace>Microsoft.SqlTools.Hosting</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'==''">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
46
src/Microsoft.SqlTools.Hosting/Properties/AssemblyInfo.cs
Normal file
46
src/Microsoft.SqlTools.Hosting/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("SqlTools Hosting Library")]
|
||||
[assembly: AssemblyDescription("Provides hosting services for SqlTools applications.")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("SqlTools Hosting Library")]
|
||||
[assembly: AssemblyCopyright("<22> Microsoft Corporation. All rights reserved.")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("6ECAFE73-131A-4221-AA13-C9BDE07FD92B")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: AssemblyInformationalVersion("1.0.0.0")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceLayer.Test")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceLayer.IntegrationTests")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceLayer.Test.Common")]
|
||||
92
src/Microsoft.SqlTools.Hosting/SqlContext/HostDetails.cs
Normal file
92
src/Microsoft.SqlTools.Hosting/SqlContext/HostDetails.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.SqlContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains details about the current host application (most
|
||||
/// likely the editor which is using the host process).
|
||||
/// </summary>
|
||||
public class HostDetails
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// The default host name for SqlTools Editor Services. Used
|
||||
/// if no host name is specified by the host application.
|
||||
/// </summary>
|
||||
public const string DefaultHostName = "SqlTools Service Host";
|
||||
|
||||
/// <summary>
|
||||
/// The default host ID for SqlTools Editor Services. Used
|
||||
/// for the host-specific profile path if no host ID is specified.
|
||||
/// </summary>
|
||||
public const string DefaultHostProfileId = "Microsoft.SqlTools.Hosting";
|
||||
|
||||
/// <summary>
|
||||
/// The default host version for SqlTools Editor Services. If
|
||||
/// no version is specified by the host application, we use 0.0.0
|
||||
/// to indicate a lack of version.
|
||||
/// </summary>
|
||||
public static readonly Version DefaultHostVersion = new Version("0.0.0");
|
||||
|
||||
/// <summary>
|
||||
/// The default host details in a HostDetails object.
|
||||
/// </summary>
|
||||
public static readonly HostDetails Default = new HostDetails(null, null, null);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the host.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the profile ID of the host, used to determine the
|
||||
/// host-specific profile path.
|
||||
/// </summary>
|
||||
public string ProfileId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of the host.
|
||||
/// </summary>
|
||||
public Version Version { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the HostDetails class.
|
||||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// The display name for the host, typically in the form of
|
||||
/// "[Application Name] Host".
|
||||
/// </param>
|
||||
/// <param name="profileId">
|
||||
/// The identifier of the SqlTools host to use for its profile path.
|
||||
/// loaded. Used to resolve a profile path of the form 'X_profile.ps1'
|
||||
/// where 'X' represents the value of hostProfileId. If null, a default
|
||||
/// will be used.
|
||||
/// </param>
|
||||
/// <param name="version">The host application's version.</param>
|
||||
public HostDetails(
|
||||
string name = null,
|
||||
string profileId = null,
|
||||
Version version = null)
|
||||
{
|
||||
this.Name = name ?? DefaultHostName;
|
||||
this.ProfileId = profileId ?? DefaultHostProfileId;
|
||||
this.Version = version ?? DefaultHostVersion;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
src/Microsoft.SqlTools.Hosting/SqlContext/SqlToolsContext.cs
Normal file
33
src/Microsoft.SqlTools.Hosting/SqlContext/SqlToolsContext.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.SqlContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Context for SQL Tools
|
||||
/// </summary>
|
||||
public class SqlToolsContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the PowerShell version of the current runspace.
|
||||
/// </summary>
|
||||
public Version SqlToolsVersion
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initalizes the SQL Tools context instance
|
||||
/// </summary>
|
||||
/// <param name="hostDetails"></param>
|
||||
public SqlToolsContext(HostDetails hostDetails)
|
||||
{
|
||||
this.SqlToolsVersion = hostDetails.Version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
52
src/Microsoft.SqlTools.Hosting/Utility/AsyncContext.cs
Normal file
52
src/Microsoft.SqlTools.Hosting/Utility/AsyncContext.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Simplifies the setup of a SynchronizationContext for the use
|
||||
/// of async calls in the current thread.
|
||||
/// </summary>
|
||||
public static class AsyncContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts a new ThreadSynchronizationContext, attaches it to
|
||||
/// the thread, and then runs the given async main function.
|
||||
/// </summary>
|
||||
/// <param name="asyncMainFunc">
|
||||
/// The Task-returning Func which represents the "main" function
|
||||
/// for the thread.
|
||||
/// </param>
|
||||
public static void Start(Func<Task> asyncMainFunc)
|
||||
{
|
||||
// Is there already a synchronization context?
|
||||
if (SynchronizationContext.Current != null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"A SynchronizationContext is already assigned on this thread.");
|
||||
}
|
||||
|
||||
// Create and register a synchronization context for this thread
|
||||
var threadSyncContext = new ThreadSynchronizationContext();
|
||||
SynchronizationContext.SetSynchronizationContext(threadSyncContext);
|
||||
|
||||
// Get the main task and act on its completion
|
||||
Task asyncMainTask = asyncMainFunc();
|
||||
asyncMainTask.ContinueWith(
|
||||
t => threadSyncContext.EndLoop(),
|
||||
TaskScheduler.Default);
|
||||
|
||||
// Start the synchronization context's request loop and
|
||||
// wait for the main task to complete
|
||||
threadSyncContext.RunLoopOnCurrentThread();
|
||||
asyncMainTask.GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
85
src/Microsoft.SqlTools.Hosting/Utility/AsyncContextThread.cs
Normal file
85
src/Microsoft.SqlTools.Hosting/Utility/AsyncContextThread.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a simplified interface for creating a new thread
|
||||
/// and establishing an AsyncContext in it.
|
||||
/// </summary>
|
||||
public class AsyncContextThread
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private Task threadTask;
|
||||
private string threadName;
|
||||
private CancellationTokenSource threadCancellationToken =
|
||||
new CancellationTokenSource();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AsyncContextThread class.
|
||||
/// </summary>
|
||||
/// <param name="threadName">
|
||||
/// The name of the thread for debugging purposes.
|
||||
/// </param>
|
||||
public AsyncContextThread(string threadName)
|
||||
{
|
||||
this.threadName = threadName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Runs a task on the AsyncContextThread.
|
||||
/// </summary>
|
||||
/// <param name="taskReturningFunc">
|
||||
/// A Func which returns the task to be run on the thread.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A Task which can be used to monitor the thread for completion.
|
||||
/// </returns>
|
||||
public Task Run(Func<Task> taskReturningFunc)
|
||||
{
|
||||
// Start up a long-running task with the action as the
|
||||
// main entry point for the thread
|
||||
this.threadTask =
|
||||
Task.Factory.StartNew(
|
||||
() =>
|
||||
{
|
||||
// Set the thread's name to help with debugging
|
||||
Thread.CurrentThread.Name = "AsyncContextThread: " + this.threadName;
|
||||
|
||||
// Set up an AsyncContext to run the task
|
||||
AsyncContext.Start(taskReturningFunc);
|
||||
},
|
||||
this.threadCancellationToken.Token,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default);
|
||||
|
||||
return this.threadTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the thread task.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
this.threadCancellationToken.Cancel();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
103
src/Microsoft.SqlTools.Hosting/Utility/AsyncLock.cs
Normal file
103
src/Microsoft.SqlTools.Hosting/Utility/AsyncLock.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a simple wrapper over a SemaphoreSlim to allow
|
||||
/// synchronization locking inside of async calls. Cannot be
|
||||
/// used recursively.
|
||||
/// </summary>
|
||||
public class AsyncLock
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private Task<IDisposable> lockReleaseTask;
|
||||
private SemaphoreSlim lockSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AsyncLock class.
|
||||
/// </summary>
|
||||
public AsyncLock()
|
||||
{
|
||||
this.lockReleaseTask =
|
||||
Task.FromResult(
|
||||
(IDisposable)new LockReleaser(this));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Locks
|
||||
/// </summary>
|
||||
/// <returns>A task which has an IDisposable</returns>
|
||||
public Task<IDisposable> LockAsync()
|
||||
{
|
||||
return this.LockAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtains or waits for a lock which can be used to synchronize
|
||||
/// access to a resource. The wait may be cancelled with the
|
||||
/// given CancellationToken.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">
|
||||
/// A CancellationToken which can be used to cancel the lock.
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public Task<IDisposable> LockAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Task waitTask = lockSemaphore.WaitAsync(cancellationToken);
|
||||
|
||||
return waitTask.IsCompleted ?
|
||||
this.lockReleaseTask :
|
||||
waitTask.ContinueWith(
|
||||
(t, releaser) =>
|
||||
{
|
||||
return (IDisposable)releaser;
|
||||
},
|
||||
this.lockReleaseTask.Result,
|
||||
cancellationToken,
|
||||
TaskContinuationOptions.ExecuteSynchronously,
|
||||
TaskScheduler.Default);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Classes
|
||||
|
||||
/// <summary>
|
||||
/// Provides an IDisposable wrapper around an AsyncLock so
|
||||
/// that it can easily be used inside of a 'using' block.
|
||||
/// </summary>
|
||||
private class LockReleaser : IDisposable
|
||||
{
|
||||
private AsyncLock lockToRelease;
|
||||
|
||||
internal LockReleaser(AsyncLock lockToRelease)
|
||||
{
|
||||
this.lockToRelease = lockToRelease;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.lockToRelease.lockSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
155
src/Microsoft.SqlTools.Hosting/Utility/AsyncQueue.cs
Normal file
155
src/Microsoft.SqlTools.Hosting/Utility/AsyncQueue.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a synchronized queue which can be used from within async
|
||||
/// operations. This is primarily used for producer/consumer scenarios.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of item contained in the queue.</typeparam>
|
||||
public class AsyncQueue<T>
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private AsyncLock queueLock = new AsyncLock();
|
||||
private Queue<T> itemQueue;
|
||||
private Queue<TaskCompletionSource<T>> requestQueue;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the queue is currently empty.
|
||||
/// </summary>
|
||||
public bool IsEmpty { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an empty instance of the AsyncQueue class.
|
||||
/// </summary>
|
||||
public AsyncQueue() : this(Enumerable.Empty<T>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of the AsyncQueue class, pre-populated
|
||||
/// with the given collection of items.
|
||||
/// </summary>
|
||||
/// <param name="initialItems">
|
||||
/// An IEnumerable containing the initial items with which the queue will
|
||||
/// be populated.
|
||||
/// </param>
|
||||
public AsyncQueue(IEnumerable<T> initialItems)
|
||||
{
|
||||
this.itemQueue = new Queue<T>(initialItems);
|
||||
this.requestQueue = new Queue<TaskCompletionSource<T>>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues an item onto the end of the queue.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to be added to the queue.</param>
|
||||
/// <returns>
|
||||
/// A Task which can be awaited until the synchronized enqueue
|
||||
/// operation completes.
|
||||
/// </returns>
|
||||
public async Task EnqueueAsync(T item)
|
||||
{
|
||||
using (await queueLock.LockAsync())
|
||||
{
|
||||
TaskCompletionSource<T> requestTaskSource = null;
|
||||
|
||||
// Are any requests waiting?
|
||||
while (this.requestQueue.Count > 0)
|
||||
{
|
||||
// Is the next request cancelled already?
|
||||
requestTaskSource = this.requestQueue.Dequeue();
|
||||
if (!requestTaskSource.Task.IsCanceled)
|
||||
{
|
||||
// Dispatch the item
|
||||
requestTaskSource.SetResult(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No more requests waiting, queue the item for a later request
|
||||
this.itemQueue.Enqueue(item);
|
||||
this.IsEmpty = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeues an item from the queue or waits asynchronously
|
||||
/// until an item is available.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A Task which can be awaited until a value can be dequeued.
|
||||
/// </returns>
|
||||
public Task<T> DequeueAsync()
|
||||
{
|
||||
return this.DequeueAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeues an item from the queue or waits asynchronously
|
||||
/// until an item is available. The wait can be cancelled
|
||||
/// using the given CancellationToken.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">
|
||||
/// A CancellationToken with which a dequeue wait can be cancelled.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A Task which can be awaited until a value can be dequeued.
|
||||
/// </returns>
|
||||
public async Task<T> DequeueAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Task<T> requestTask;
|
||||
|
||||
using (await queueLock.LockAsync(cancellationToken))
|
||||
{
|
||||
if (this.itemQueue.Count > 0)
|
||||
{
|
||||
// Items are waiting to be taken so take one immediately
|
||||
T item = this.itemQueue.Dequeue();
|
||||
this.IsEmpty = this.itemQueue.Count == 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Queue the request for the next item
|
||||
var requestTaskSource = new TaskCompletionSource<T>();
|
||||
this.requestQueue.Enqueue(requestTaskSource);
|
||||
|
||||
// Register the wait task for cancel notifications
|
||||
cancellationToken.Register(
|
||||
() => requestTaskSource.TrySetCanceled());
|
||||
|
||||
requestTask = requestTaskSource.Task;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the request task to complete outside of the lock
|
||||
return await requestTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
59
src/Microsoft.SqlTools.Hosting/Utility/Extensions.cs
Normal file
59
src/Microsoft.SqlTools.Hosting/Utility/Extensions.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
{
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension to evaluate an object's ToString() method in an exception safe way. This will
|
||||
/// extension method will not throw.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object on which to call ToString()</param>
|
||||
/// <returns>The ToString() return value or a suitable error message is that throws.</returns>
|
||||
public static string SafeToString(this object obj)
|
||||
{
|
||||
string str;
|
||||
|
||||
try
|
||||
{
|
||||
str = obj.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
str = $"<Error converting poperty value to string - {ex.Message}>";
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a boolean to a "1" or "0" string. Particularly helpful when sending telemetry
|
||||
/// </summary>
|
||||
public static string ToOneOrZeroString(this bool isTrue)
|
||||
{
|
||||
return isTrue ? "1" : "0";
|
||||
}
|
||||
}
|
||||
|
||||
public static class NullableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension method to evaluate a bool? and determine if it has the value and is true.
|
||||
/// This way we avoid throwing if the bool? doesn't have a value.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <c>bool?</c> to process</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if <paramref name="obj"/> has a value and it is <c>true</c>
|
||||
/// <c>false</c> otherwise.
|
||||
/// </returns>
|
||||
public static bool HasTrue(this bool? obj)
|
||||
{
|
||||
return obj.HasValue && obj.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
271
src/Microsoft.SqlTools.Hosting/Utility/Logger.cs
Normal file
271
src/Microsoft.SqlTools.Hosting/Utility/Logger.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
//
|
||||
// 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the level indicators for log messages.
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates a verbose log message.
|
||||
/// </summary>
|
||||
Verbose,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates a normal, non-verbose log message.
|
||||
/// </summary>
|
||||
Normal,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates a warning message.
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates an error message.
|
||||
/// </summary>
|
||||
Error
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a simple logging interface. May be replaced with a
|
||||
/// more robust solution at a later date.
|
||||
/// </summary>
|
||||
public static class Logger
|
||||
{
|
||||
private static LogWriter logWriter;
|
||||
|
||||
private static bool isEnabled;
|
||||
|
||||
private static bool isInitialized = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Logger for the current session.
|
||||
/// </summary>
|
||||
/// <param name="logFilePath">
|
||||
/// Optional. Specifies the path at which log messages will be written.
|
||||
/// </param>
|
||||
/// <param name="minimumLogLevel">
|
||||
/// Optional. Specifies the minimum log message level to write to the log file.
|
||||
/// </param>
|
||||
public static void Initialize(
|
||||
string logFilePath = "sqltools",
|
||||
LogLevel minimumLogLevel = LogLevel.Normal,
|
||||
bool isEnabled = true)
|
||||
{
|
||||
Logger.isEnabled = isEnabled;
|
||||
|
||||
// return if the logger is not enabled or already initialized
|
||||
if (!Logger.isEnabled || Logger.isInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.isInitialized = true;
|
||||
|
||||
// get a unique number to prevent conflicts of two process launching at the same time
|
||||
int uniqueId;
|
||||
try
|
||||
{
|
||||
uniqueId = Process.GetCurrentProcess().Id;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// if the pid look up fails for any reason, just use a random number
|
||||
uniqueId = new Random().Next(1000, 9999);
|
||||
}
|
||||
|
||||
// make the log path unique
|
||||
string fullFileName = string.Format(
|
||||
"{0}_{1,4:D4}{2,2:D2}{3,2:D2}{4,2:D2}{5,2:D2}{6,2:D2}{7}.log",
|
||||
logFilePath,
|
||||
DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day,
|
||||
DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second,
|
||||
uniqueId);
|
||||
|
||||
if (logWriter != null)
|
||||
{
|
||||
logWriter.Dispose();
|
||||
}
|
||||
|
||||
// TODO: Parameterize this
|
||||
logWriter =
|
||||
new LogWriter(
|
||||
minimumLogLevel,
|
||||
fullFileName,
|
||||
true);
|
||||
|
||||
Logger.Write(LogLevel.Normal, "Initializing SQL Tools Service Host logger");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the Logger.
|
||||
/// </summary>
|
||||
public static void Close()
|
||||
{
|
||||
if (logWriter != null)
|
||||
{
|
||||
logWriter.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a message to the log file.
|
||||
/// </summary>
|
||||
/// <param name="logLevel">The level at which the message will be written.</param>
|
||||
/// <param name="logMessage">The message text to be written.</param>
|
||||
/// <param name="callerName">The name of the calling method.</param>
|
||||
/// <param name="callerSourceFile">The source file path where the calling method exists.</param>
|
||||
/// <param name="callerLineNumber">The line number of the calling method.</param>
|
||||
public static void Write(
|
||||
LogLevel logLevel,
|
||||
string logMessage,
|
||||
[CallerMemberName] string callerName = null,
|
||||
[CallerFilePath] string callerSourceFile = null,
|
||||
[CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
// return if the logger is not enabled or not initialized
|
||||
if (!Logger.isEnabled || !Logger.isInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (logWriter != null)
|
||||
{
|
||||
logWriter.Write(
|
||||
logLevel,
|
||||
logMessage,
|
||||
callerName,
|
||||
callerSourceFile,
|
||||
callerLineNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class LogWriter : IDisposable
|
||||
{
|
||||
private object logLock = new object();
|
||||
private TextWriter textWriter;
|
||||
private LogLevel minimumLogLevel = LogLevel.Verbose;
|
||||
|
||||
public LogWriter(LogLevel minimumLogLevel, string logFilePath, bool deleteExisting)
|
||||
{
|
||||
this.minimumLogLevel = minimumLogLevel;
|
||||
|
||||
// Ensure that we have a usable log file path
|
||||
if (!Path.IsPathRooted(logFilePath))
|
||||
{
|
||||
logFilePath =
|
||||
Path.Combine(
|
||||
AppContext.BaseDirectory,
|
||||
logFilePath);
|
||||
}
|
||||
|
||||
|
||||
if (!this.TryOpenLogFile(logFilePath, deleteExisting))
|
||||
{
|
||||
// If the log file couldn't be opened at this location,
|
||||
// try opening it in a more reliable path
|
||||
this.TryOpenLogFile(
|
||||
Path.Combine(
|
||||
Environment.GetEnvironmentVariable("TEMP"),
|
||||
Path.GetFileName(logFilePath)),
|
||||
deleteExisting);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(
|
||||
LogLevel logLevel,
|
||||
string logMessage,
|
||||
string callerName = null,
|
||||
string callerSourceFile = null,
|
||||
int callerLineNumber = 0)
|
||||
{
|
||||
if (this.textWriter != null &&
|
||||
logLevel >= this.minimumLogLevel)
|
||||
{
|
||||
// System.IO is not thread safe
|
||||
lock (this.logLock)
|
||||
{
|
||||
// Print the timestamp and log level
|
||||
this.textWriter.WriteLine(
|
||||
"{0} [{1}] - Method \"{2}\" at line {3} of {4}\r\n",
|
||||
DateTime.Now,
|
||||
logLevel.ToString().ToUpper(),
|
||||
callerName,
|
||||
callerLineNumber,
|
||||
callerSourceFile);
|
||||
|
||||
// Print out indented message lines
|
||||
foreach (var messageLine in logMessage.Split('\n'))
|
||||
{
|
||||
this.textWriter.WriteLine(" " + messageLine.TrimEnd());
|
||||
}
|
||||
|
||||
// Finish with a newline and flush the writer
|
||||
this.textWriter.WriteLine();
|
||||
this.textWriter.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (this.textWriter != null)
|
||||
{
|
||||
this.textWriter.Flush();
|
||||
this.textWriter.Dispose();
|
||||
this.textWriter = null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryOpenLogFile(
|
||||
string logFilePath,
|
||||
bool deleteExisting)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Make sure the log directory exists
|
||||
Directory.CreateDirectory(
|
||||
Path.GetDirectoryName(
|
||||
logFilePath));
|
||||
|
||||
// Open the log file for writing with UTF8 encoding
|
||||
this.textWriter =
|
||||
new StreamWriter(
|
||||
new FileStream(
|
||||
logFilePath,
|
||||
deleteExisting ?
|
||||
FileMode.Create :
|
||||
FileMode.Append),
|
||||
Encoding.UTF8);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is UnauthorizedAccessException ||
|
||||
e is IOException)
|
||||
{
|
||||
// This exception is thrown when we can't open the file
|
||||
// at the path in logFilePath. Return false to indicate
|
||||
// that the log file couldn't be created.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unexpected exception, rethrow it
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
275
src/Microsoft.SqlTools.Hosting/Utility/LongList.cs
Normal file
275
src/Microsoft.SqlTools.Hosting/Utility/LongList.cs
Normal file
@@ -0,0 +1,275 @@
|
||||
//
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection class that permits storage of over <c>int.MaxValue</c> items. This is performed
|
||||
/// by using a 2D list of lists. The internal lists are only initialized as necessary. This
|
||||
/// collection implements IEnumerable to make it easier to run LINQ queries against it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is based on code from $\Data Tools\SSMS_Main\sql\ssms\core\DataStorage\ArrayList64.cs
|
||||
/// with additions to bring it up to .NET 4.5 standards
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">Type of the values to store</typeparam>
|
||||
public class LongList<T> : IEnumerable<T>
|
||||
{
|
||||
#region Member Variables
|
||||
|
||||
private int expandListSize = int.MaxValue;
|
||||
private List<List<T>> expandedList;
|
||||
private readonly List<T> shortList;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new long list
|
||||
/// </summary>
|
||||
public LongList()
|
||||
{
|
||||
shortList = new List<T>();
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The total number of elements in the array
|
||||
/// </summary>
|
||||
public long Count { get; private set; }
|
||||
|
||||
public T this[long index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetItem(index);
|
||||
}
|
||||
}
|
||||
|
||||
public int ExpandListSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.expandListSize;
|
||||
}
|
||||
internal set
|
||||
{
|
||||
this.expandListSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified value to the end of the list
|
||||
/// </summary>
|
||||
/// <param name="val">Value to add to the list</param>
|
||||
/// <returns>Index of the item that was just added</returns>
|
||||
public long Add(T val)
|
||||
{
|
||||
if (Count <= this.ExpandListSize)
|
||||
{
|
||||
shortList.Add(val);
|
||||
}
|
||||
else // need to split values into several arrays
|
||||
{
|
||||
if (expandedList == null)
|
||||
{
|
||||
// very inefficient so delay as much as possible
|
||||
// immediately add 0th array
|
||||
expandedList = new List<List<T>> {shortList};
|
||||
}
|
||||
|
||||
int arrayIndex = (int)(Count / this.ExpandListSize); // 0 based
|
||||
|
||||
List<T> arr;
|
||||
if (expandedList.Count <= arrayIndex) // need to make a new array
|
||||
{
|
||||
arr = new List<T>();
|
||||
expandedList.Add(arr);
|
||||
}
|
||||
else // use existing array
|
||||
{
|
||||
arr = expandedList[arrayIndex];
|
||||
}
|
||||
arr.Add(val);
|
||||
}
|
||||
return (++Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the item at the specified index
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the item to return</param>
|
||||
/// <returns>The item at the index specified</returns>
|
||||
public T GetItem(long index)
|
||||
{
|
||||
T val = default(T);
|
||||
|
||||
if (Count <= this.ExpandListSize)
|
||||
{
|
||||
int i32Index = Convert.ToInt32(index);
|
||||
val = shortList[i32Index];
|
||||
}
|
||||
else
|
||||
{
|
||||
int iArray32Index = (int) (Count / this.ExpandListSize);
|
||||
if (expandedList.Count > iArray32Index)
|
||||
{
|
||||
List<T> arr = expandedList[iArray32Index];
|
||||
|
||||
int i32Index = (int) (Count % this.ExpandListSize);
|
||||
if (arr.Count > i32Index)
|
||||
{
|
||||
val = arr[i32Index];
|
||||
}
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item at the specified location and shifts all the items after the provided
|
||||
/// index up by one.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to remove from the list</param>
|
||||
public void RemoveAt(long index)
|
||||
{
|
||||
if (Count <= this.ExpandListSize)
|
||||
{
|
||||
int iArray32MemberIndex = Convert.ToInt32(index); // 0 based
|
||||
shortList.RemoveAt(iArray32MemberIndex);
|
||||
}
|
||||
else // handle the case of multiple arrays
|
||||
{
|
||||
// find out which array it is in
|
||||
int arrayIndex = (int) (index / this.ExpandListSize);
|
||||
List<T> arr = expandedList[arrayIndex];
|
||||
|
||||
// find out index into this array
|
||||
int iArray32MemberIndex = (int) (index % this.ExpandListSize);
|
||||
arr.RemoveAt(iArray32MemberIndex);
|
||||
|
||||
// now shift members of the array back one
|
||||
int iArray32TotalIndex = (int) (Count / this.ExpandListSize);
|
||||
for (int i = arrayIndex + 1; i < iArray32TotalIndex; i++)
|
||||
{
|
||||
List<T> arr1 = expandedList[i - 1];
|
||||
List<T> arr2 = expandedList[i];
|
||||
|
||||
arr1.Add(arr2[this.ExpandListSize - 1]);
|
||||
arr2.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
--Count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<object> Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Returns a generic enumerator for enumeration of this LongList
|
||||
/// </summary>
|
||||
/// <returns>Enumerator for LongList</returns>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return new LongListEnumerator<T>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator for enumeration of this LongList
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public class LongListEnumerator<TEt> : IEnumerator<TEt>
|
||||
{
|
||||
#region Member Variables
|
||||
|
||||
/// <summary>
|
||||
/// The index into the list of the item that is the current item
|
||||
/// </summary>
|
||||
private long index;
|
||||
|
||||
/// <summary>
|
||||
/// The current list that we're iterating over.
|
||||
/// </summary>
|
||||
private readonly LongList<TEt> localList;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new enumerator for a given LongList
|
||||
/// </summary>
|
||||
/// <param name="list">The list to enumerate</param>
|
||||
public LongListEnumerator(LongList<TEt> list)
|
||||
{
|
||||
localList = list;
|
||||
index = 0;
|
||||
Current = default(TEt);
|
||||
}
|
||||
|
||||
#region IEnumerator Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current item in the enumeration
|
||||
/// </summary>
|
||||
public TEt Current { get; private set; }
|
||||
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get { return Current; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves to the next item in the list we're iterating over
|
||||
/// </summary>
|
||||
/// <returns>Whether or not the move was successful</returns>
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (index < localList.Count)
|
||||
{
|
||||
Current = localList[index];
|
||||
index++;
|
||||
return true;
|
||||
}
|
||||
Current = default(TEt);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the enumeration
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
index = 0;
|
||||
Current = default(TEt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposal method. Does nothing.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
127
src/Microsoft.SqlTools.Hosting/Utility/TextUtilities.cs
Normal file
127
src/Microsoft.SqlTools.Hosting/Utility/TextUtilities.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
{
|
||||
public static class TextUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Find the position of the cursor in the SQL script content buffer and return previous new line position
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="startRow">parameter is 0-based</param>
|
||||
/// <param name="startColumn">parameter is 0-based</param>
|
||||
/// <param name="prevNewLine">parameter is 0-based</param>
|
||||
public static int PositionOfCursor(string sql, int startRow, int startColumn, out int prevNewLine)
|
||||
{
|
||||
prevNewLine = 0;
|
||||
if (string.IsNullOrWhiteSpace(sql))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < startRow; ++i)
|
||||
{
|
||||
while (prevNewLine < sql.Length && sql[prevNewLine] != '\n')
|
||||
{
|
||||
++prevNewLine;
|
||||
}
|
||||
++prevNewLine;
|
||||
}
|
||||
|
||||
return startColumn + prevNewLine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the position of the previous delimeter for autocomplete token replacement.
|
||||
/// SQL Parser may have similar functionality in which case we'll delete this method.
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="startRow">parameter is 0-based</param>
|
||||
/// <param name="startColumn">parameter is 0-based</param>
|
||||
/// <param name="tokenText"></param>
|
||||
public static int PositionOfPrevDelimeter(string sql, int startRow, int startColumn)
|
||||
{
|
||||
int prevNewLine;
|
||||
int delimeterPos = PositionOfCursor(sql, startRow, startColumn, out prevNewLine);
|
||||
|
||||
if (delimeterPos - 1 < sql.Length)
|
||||
{
|
||||
while (--delimeterPos >= prevNewLine)
|
||||
{
|
||||
if (IsCharacterDelimeter(sql[delimeterPos]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delimeterPos = delimeterPos + 1 - prevNewLine;
|
||||
}
|
||||
|
||||
return delimeterPos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the position of the next delimeter for autocomplete token replacement.
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="startRow">parameter is 0-based</param>
|
||||
/// <param name="startColumn">parameter is 0-based</param>
|
||||
public static int PositionOfNextDelimeter(string sql, int startRow, int startColumn)
|
||||
{
|
||||
int prevNewLine;
|
||||
int delimeterPos = PositionOfCursor(sql, startRow, startColumn, out prevNewLine);
|
||||
|
||||
while (delimeterPos < sql.Length)
|
||||
{
|
||||
if (IsCharacterDelimeter(sql[delimeterPos]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
++delimeterPos;
|
||||
}
|
||||
|
||||
return delimeterPos - prevNewLine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the character is a SQL token delimiter
|
||||
/// </summary>
|
||||
/// <param name="ch"></param>
|
||||
private static bool IsCharacterDelimeter(char ch)
|
||||
{
|
||||
return ch == ' '
|
||||
|| ch == '\t'
|
||||
|| ch == '\n'
|
||||
|| ch == '.'
|
||||
|| ch == '+'
|
||||
|| ch == '-'
|
||||
|| ch == '*'
|
||||
|| ch == '>'
|
||||
|| ch == '<'
|
||||
|| ch == '='
|
||||
|| ch == '/'
|
||||
|| ch == '%'
|
||||
|| ch == ','
|
||||
|| ch == ';'
|
||||
|| ch == '('
|
||||
|| ch == ')';
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove square bracket syntax from a token string
|
||||
/// </summary>
|
||||
/// <param name="tokenText"></param>
|
||||
/// <returns> string with outer brackets removed</returns>
|
||||
public static string RemoveSquareBracketSyntax(string tokenText)
|
||||
{
|
||||
if(tokenText.StartsWith("[") && tokenText.EndsWith("]"))
|
||||
{
|
||||
return tokenText.Substring(1, tokenText.Length - 2);
|
||||
}
|
||||
return tokenText;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// 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.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a SynchronizationContext implementation that can be used
|
||||
/// in console applications or any thread which doesn't have its
|
||||
/// own SynchronizationContext.
|
||||
/// </summary>
|
||||
public class ThreadSynchronizationContext : SynchronizationContext
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private BlockingCollection<Tuple<SendOrPostCallback, object>> requestQueue =
|
||||
new BlockingCollection<Tuple<SendOrPostCallback, object>>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Posts a request for execution to the SynchronizationContext.
|
||||
/// This will be executed on the SynchronizationContext's thread.
|
||||
/// </summary>
|
||||
/// <param name="callback">
|
||||
/// The callback to be invoked on the SynchronizationContext's thread.
|
||||
/// </param>
|
||||
/// <param name="state">
|
||||
/// A state object to pass along to the callback when executed through
|
||||
/// the SynchronizationContext.
|
||||
/// </param>
|
||||
public override void Post(SendOrPostCallback callback, object state)
|
||||
{
|
||||
// Add the request to the queue
|
||||
this.requestQueue.Add(
|
||||
new Tuple<SendOrPostCallback, object>(
|
||||
callback, state));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Starts the SynchronizationContext message loop on the current thread.
|
||||
/// </summary>
|
||||
public void RunLoopOnCurrentThread()
|
||||
{
|
||||
Tuple<SendOrPostCallback, object> request;
|
||||
|
||||
while (this.requestQueue.TryTake(out request, Timeout.Infinite))
|
||||
{
|
||||
// Invoke the request's callback
|
||||
request.Item1(request.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the SynchronizationContext message loop.
|
||||
/// </summary>
|
||||
public void EndLoop()
|
||||
{
|
||||
// Tell the blocking queue that we're done
|
||||
this.requestQueue.CompleteAdding();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
158
src/Microsoft.SqlTools.Hosting/Utility/Validate.cs
Normal file
158
src/Microsoft.SqlTools.Hosting/Utility/Validate.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides common validation methods to simplify method
|
||||
/// parameter checks.
|
||||
/// </summary>
|
||||
public static class Validate
|
||||
{
|
||||
/// <summary>
|
||||
/// Throws ArgumentNullException if value is null.
|
||||
/// </summary>
|
||||
/// <param name="parameterName">The name of the parameter being validated.</param>
|
||||
/// <param name="valueToCheck">The value of the parameter being validated.</param>
|
||||
public static void IsNotNull(string parameterName, object valueToCheck)
|
||||
{
|
||||
if (valueToCheck == null)
|
||||
{
|
||||
throw new ArgumentNullException(parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws ArgumentOutOfRangeException if the value is outside
|
||||
/// of the given lower and upper limits.
|
||||
/// </summary>
|
||||
/// <param name="parameterName">The name of the parameter being validated.</param>
|
||||
/// <param name="valueToCheck">The value of the parameter being validated.</param>
|
||||
/// <param name="lowerLimit">The lower limit which the value should not be less than.</param>
|
||||
/// <param name="upperLimit">The upper limit which the value should not be greater than.</param>
|
||||
public static void IsWithinRange(
|
||||
string parameterName,
|
||||
int valueToCheck,
|
||||
int lowerLimit,
|
||||
int upperLimit)
|
||||
{
|
||||
// TODO: Debug assert here if lowerLimit >= upperLimit
|
||||
|
||||
if (valueToCheck < lowerLimit || valueToCheck > upperLimit)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
parameterName,
|
||||
valueToCheck,
|
||||
string.Format(
|
||||
"Value is not between {0} and {1}",
|
||||
lowerLimit,
|
||||
upperLimit));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws ArgumentOutOfRangeException if the value is greater than or equal
|
||||
/// to the given upper limit.
|
||||
/// </summary>
|
||||
/// <param name="parameterName">The name of the parameter being validated.</param>
|
||||
/// <param name="valueToCheck">The value of the parameter being validated.</param>
|
||||
/// <param name="upperLimit">The upper limit which the value should be less than.</param>
|
||||
public static void IsLessThan(
|
||||
string parameterName,
|
||||
int valueToCheck,
|
||||
int upperLimit)
|
||||
{
|
||||
if (valueToCheck >= upperLimit)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
parameterName,
|
||||
valueToCheck,
|
||||
string.Format(
|
||||
"Value is greater than or equal to {0}",
|
||||
upperLimit));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws ArgumentOutOfRangeException if the value is less than or equal
|
||||
/// to the given lower limit.
|
||||
/// </summary>
|
||||
/// <param name="parameterName">The name of the parameter being validated.</param>
|
||||
/// <param name="valueToCheck">The value of the parameter being validated.</param>
|
||||
/// <param name="lowerLimit">The lower limit which the value should be greater than.</param>
|
||||
public static void IsGreaterThan(
|
||||
string parameterName,
|
||||
int valueToCheck,
|
||||
int lowerLimit)
|
||||
{
|
||||
if (valueToCheck < lowerLimit)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
parameterName,
|
||||
valueToCheck,
|
||||
string.Format(
|
||||
"Value is less than or equal to {0}",
|
||||
lowerLimit));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws ArgumentException if the value is equal to the undesired value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of value to be validated.</typeparam>
|
||||
/// <param name="parameterName">The name of the parameter being validated.</param>
|
||||
/// <param name="undesiredValue">The value that valueToCheck should not equal.</param>
|
||||
/// <param name="valueToCheck">The value of the parameter being validated.</param>
|
||||
public static void IsNotEqual<TValue>(
|
||||
string parameterName,
|
||||
TValue valueToCheck,
|
||||
TValue undesiredValue)
|
||||
{
|
||||
if (EqualityComparer<TValue>.Default.Equals(valueToCheck, undesiredValue))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
"The given value '{0}' should not equal '{1}'",
|
||||
valueToCheck,
|
||||
undesiredValue),
|
||||
parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws ArgumentException if the value is null or an empty string.
|
||||
/// </summary>
|
||||
/// <param name="parameterName">The name of the parameter being validated.</param>
|
||||
/// <param name="valueToCheck">The value of the parameter being validated.</param>
|
||||
public static void IsNotNullOrEmptyString(string parameterName, string valueToCheck)
|
||||
{
|
||||
if (string.IsNullOrEmpty(valueToCheck))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Parameter contains a null, empty, or whitespace string.",
|
||||
parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws ArgumentException if the value is null, an empty string,
|
||||
/// or a string containing only whitespace.
|
||||
/// </summary>
|
||||
/// <param name="parameterName">The name of the parameter being validated.</param>
|
||||
/// <param name="valueToCheck">The value of the parameter being validated.</param>
|
||||
public static void IsNotNullOrWhitespaceString(string parameterName, string valueToCheck)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(valueToCheck))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Parameter contains a null, empty, or whitespace string.",
|
||||
parameterName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
src/Microsoft.SqlTools.Hosting/project.json
Normal file
59
src/Microsoft.SqlTools.Hosting/project.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "Microsoft.SqlTools.Hosting",
|
||||
"version": "1.0.0-*",
|
||||
"buildOptions": {
|
||||
"debugType": "portable",
|
||||
"emitEntryPoint": false
|
||||
},
|
||||
"configurations": {
|
||||
"Integration": {
|
||||
"buildOptions": {
|
||||
"define": [
|
||||
"WINDOWS_ONLY_BUILD"
|
||||
],
|
||||
"emitEntryPoint": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "9.0.1",
|
||||
"System.Collections.Specialized": "4.0.1",
|
||||
"System.ComponentModel.TypeConverter": "4.1.0",
|
||||
"System.Diagnostics.Contracts": "4.0.1",
|
||||
"System.Diagnostics.TraceSource": "4.0.0",
|
||||
"NETStandard.Library": "1.6.0",
|
||||
"Microsoft.NETCore.Runtime.CoreCLR": "1.0.2",
|
||||
"Microsoft.NETCore.DotNetHostPolicy": "1.0.1",
|
||||
"Microsoft.DiaSymReader.Native": "1.4.1",
|
||||
"System.Diagnostics.Process": "4.1.0",
|
||||
"System.Threading.Thread": "4.0.0",
|
||||
"System.Runtime.Loader": "4.0.0",
|
||||
"System.Composition.Runtime": "1.0.31",
|
||||
"System.Composition.Convention": "1.0.31",
|
||||
"System.Composition.TypedParts": "1.0.31",
|
||||
"Microsoft.Extensions.DependencyModel": "1.0.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
},
|
||||
"frameworks": {
|
||||
"netcoreapp1.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
},
|
||||
"imports": "dnxcore50"
|
||||
}
|
||||
},
|
||||
"runtimes": {
|
||||
"win7-x64": {},
|
||||
"win7-x86": {},
|
||||
"osx.10.11-x64": {},
|
||||
"ubuntu.14.04-x64": {},
|
||||
"ubuntu.16.04-x64": {},
|
||||
"centos.7-x64": {},
|
||||
"rhel.7.2-x64": {},
|
||||
"debian.8-x64": {},
|
||||
"fedora.23-x64": {},
|
||||
"opensuse.13.2-x64": {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user