mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-06 09:35:39 -05:00
Merge branch 'dev' of github.com:Microsoft/sqltoolsservice into dev
This commit is contained in:
@@ -39,5 +39,6 @@ script:
|
||||
- dotnet test test/Microsoft.SqlTools.ServiceLayer.Test
|
||||
|
||||
env:
|
||||
- ProjectPath=home/travis/build/Microsoft/sqltoolsservice
|
||||
# Since we are building from root, current directory is the project path
|
||||
- ProjectPath=./
|
||||
|
||||
BIN
bin/nuget/mssql.ResX.nupkg
Normal file
BIN
bin/nuget/mssql.ResX.nupkg
Normal file
Binary file not shown.
BIN
bin/nuget/mssql.XliffParser.nupkg
Normal file
BIN
bin/nuget/mssql.XliffParser.nupkg
Normal file
Binary file not shown.
82
build.cake
82
build.cake
@@ -1,4 +1,6 @@
|
||||
#addin "Newtonsoft.Json"
|
||||
#addin "mssql.ResX"
|
||||
#addin "mssql.XliffParser"
|
||||
|
||||
#load "scripts/runhelpers.cake"
|
||||
#load "scripts/archiving.cake"
|
||||
@@ -9,7 +11,8 @@ using System.ComponentModel;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Cake.Common.IO
|
||||
using Cake.Common.IO;
|
||||
using XliffParser;
|
||||
|
||||
// Basic arguments
|
||||
var target = Argument("target", "Default");
|
||||
@@ -510,39 +513,58 @@ Task("SetPackageVersions")
|
||||
Task("SRGen")
|
||||
.Does(() =>
|
||||
{
|
||||
var projects = System.IO.Directory.GetFiles(sourceFolder, "project.json", SearchOption.AllDirectories).ToList();
|
||||
foreach(var project in projects) {
|
||||
var projectDir = System.IO.Path.GetDirectoryName(project);
|
||||
var projectName = (new System.IO.DirectoryInfo(projectDir)).Name;
|
||||
var projectStrings = System.IO.Path.Combine(projectDir, "sr.strings");
|
||||
var projects = System.IO.Directory.GetFiles(sourceFolder, "project.json", SearchOption.AllDirectories).ToList();
|
||||
|
||||
if (!System.IO.File.Exists(projectStrings))
|
||||
{
|
||||
Information("Project {0} doesn't contain 'sr.strings' file", projectName);
|
||||
continue;
|
||||
}
|
||||
foreach(var project in projects) {
|
||||
var projectDir = System.IO.Path.GetDirectoryName(project);
|
||||
var localizationDir = System.IO.Path.Combine(projectDir, "Localization");
|
||||
var projectName = (new System.IO.DirectoryInfo(projectDir)).Name;
|
||||
var projectNameSpace = projectName + ".Localization";
|
||||
var projectStrings = System.IO.Path.Combine(localizationDir, "sr.strings");
|
||||
|
||||
var srgenPath = System.IO.Path.Combine(toolsFolder, "Microsoft.DataTools.SrGen", "lib", "netcoreapp1.0", "srgen.dll");
|
||||
var outputResx = System.IO.Path.Combine(projectDir, "sr.resx");
|
||||
var outputCs = System.IO.Path.Combine(projectDir, "sr.cs");
|
||||
if (!System.IO.File.Exists(projectStrings))
|
||||
{
|
||||
Information("Project {0} doesn't contain 'sr.strings' file", projectName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Delete preexisting resx and designer files
|
||||
if (System.IO.File.Exists(outputResx))
|
||||
{
|
||||
System.IO.File.Delete(outputResx);
|
||||
}
|
||||
if (System.IO.File.Exists(outputCs))
|
||||
{
|
||||
System.IO.File.Delete(outputCs);
|
||||
}
|
||||
var srgenPath = System.IO.Path.Combine(toolsFolder, "Microsoft.DataTools.SrGen", "lib", "netcoreapp1.0", "srgen.dll");
|
||||
var outputResx = System.IO.Path.Combine(localizationDir, "sr.resx");
|
||||
var inputXliff = System.IO.Path.Combine(localizationDir, "transXliff");
|
||||
var outputXlf = System.IO.Path.Combine(localizationDir, "sr.xlf");
|
||||
var outputCs = System.IO.Path.Combine(localizationDir, "sr.cs");
|
||||
|
||||
// Run SRGen
|
||||
var dotnetArgs = string.Format("{0} -or \"{1}\" -oc \"{2}\" -ns \"{3}\" -an \"{4}\" -cn SR -l CS -dnx \"{5}\"",
|
||||
srgenPath, outputResx, outputCs, projectName, projectName, projectStrings);
|
||||
Information("{0}", dotnetArgs);
|
||||
Run(dotnetcli, dotnetArgs)
|
||||
.ExceptionOnError("Failed to run SRGen.");
|
||||
}
|
||||
// Delete preexisting resx and designer files
|
||||
if (System.IO.File.Exists(outputResx))
|
||||
{
|
||||
System.IO.File.Delete(outputResx);
|
||||
}
|
||||
if (System.IO.File.Exists(outputCs))
|
||||
{
|
||||
System.IO.File.Delete(outputCs);
|
||||
}
|
||||
|
||||
// Run SRGen
|
||||
var dotnetArgs = string.Format("{0} -or \"{1}\" -oc \"{2}\" -ns \"{3}\" -an \"{4}\" -cn SR -l CS -dnx \"{5}\"",
|
||||
srgenPath, outputResx, outputCs, projectName, projectNameSpace, projectStrings);
|
||||
Information("{0}", dotnetArgs);
|
||||
Run(dotnetcli, dotnetArgs)
|
||||
.ExceptionOnError("Failed to run SRGen.");
|
||||
|
||||
// Update XLF file from new Resx file
|
||||
var doc = new XliffParser.XlfDocument(outputXlf);
|
||||
doc.UpdateFromSource();
|
||||
doc.Save();
|
||||
|
||||
// Update ResX files from new xliff files
|
||||
var xlfDocNames = System.IO.Directory.GetFiles(inputXliff, "*.xlf", SearchOption.AllDirectories).ToList();
|
||||
foreach(var docName in xlfDocNames)
|
||||
{
|
||||
var xlfDoc = new XliffParser.XlfDocument(docName);
|
||||
var newPath = System.IO.Path.Combine(localizationDir, System.IO.Path.GetFileName(docName));
|
||||
xlfDoc.SaveAsResX(newPath.Replace("xlf","resx"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -89,6 +89,7 @@ if(-Not $SkipToolPackageRestore.IsPresent)
|
||||
Set-Location $TOOLS_DIR
|
||||
|
||||
Write-Verbose -Message "Restoring tools from NuGet..."
|
||||
$NuGetConfig = Invoke-Expression "&`"$NUGET_EXE`" config -configfile ../nuget.config"
|
||||
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install $PACKAGES_CONFIG -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
|
||||
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ fi
|
||||
|
||||
# Restore tools from NuGet.
|
||||
pushd "$TOOLS_DIR" >/dev/null
|
||||
mono "$NUGET_EXE" config -configfile ../nuget.config
|
||||
mono "$NUGET_EXE" install "$PACKAGES_CONFIG" -ExcludeVersion -OutputDirectory "$TOOLS_DIR"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Could not restore NuGet packages."
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
<package id="Newtonsoft.Json" version="8.0.3" />
|
||||
<package id="Microsoft.DataTools.SrGen" version="1.0.2" />
|
||||
<package id="Mono.TextTransform" version="1.0.0" />
|
||||
<package id="mssql.XliffParser" version="0.5.3" />
|
||||
</packages>
|
||||
@@ -328,7 +328,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Ignore
|
||||
// If ObjectDisposedException was thrown, then execution has already exited the
|
||||
// "using" statment and source was disposed, meaning that the openTask completed
|
||||
// successfully. This results in a ObjectDisposedException when trying to access
|
||||
// source.Token and should be ignored.
|
||||
}
|
||||
});
|
||||
|
||||
@@ -720,7 +723,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
}
|
||||
|
||||
// open connection based on request details
|
||||
result = await Instance.Connect(connectParams);
|
||||
result = await Connect(connectParams);
|
||||
await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -745,7 +748,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
|
||||
try
|
||||
{
|
||||
bool result = Instance.CancelConnect(cancelParams);
|
||||
bool result = CancelConnect(cancelParams);
|
||||
await requestContext.SendResult(result);
|
||||
}
|
||||
catch(Exception ex)
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
|
||||
try
|
||||
{
|
||||
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString);
|
||||
Debug.Assert(builder.Pooling == false, "Pooling should be false");
|
||||
Debug.Assert(!builder.Pooling, "Pooling should be false");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -63,7 +63,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
|
||||
}
|
||||
#endif
|
||||
|
||||
if (AmbientSettings.AlwaysRetryOnTransientFailure == true)
|
||||
if (AmbientSettings.AlwaysRetryOnTransientFailure)
|
||||
{
|
||||
useRetry = true;
|
||||
}
|
||||
@@ -368,8 +368,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
|
||||
IDbCommand cmd = null;
|
||||
try
|
||||
{
|
||||
Debug.Assert(conn.State == ConnectionState.Open, "connection passed to ExecuteReader should be open.");
|
||||
|
||||
cmd = conn.CreateCommand();
|
||||
|
||||
if (initializeCommand == null)
|
||||
@@ -603,11 +601,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
|
||||
Validate.IsNotNullOrEmptyString(nameof(commandText), commandText);
|
||||
|
||||
string filePath = ExecuteScalar(conn, commandText, initializeCommand, catchException) as string;
|
||||
if (!String.IsNullOrWhiteSpace(filePath))
|
||||
if (!string.IsNullOrWhiteSpace(filePath))
|
||||
{
|
||||
// Remove filename from the filePath
|
||||
Uri pathUri;
|
||||
if (Uri.TryCreate(filePath, UriKind.Absolute, out pathUri) == false)
|
||||
if (!Uri.TryCreate(filePath, UriKind.Absolute, out pathUri))
|
||||
{
|
||||
// Invalid Uri
|
||||
return null;
|
||||
@@ -931,7 +929,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
|
||||
|
||||
serverInfo = GetServerVersion(connection);
|
||||
|
||||
tempDatabaseName = (String.IsNullOrEmpty(builder.InitialCatalog) == false) ?
|
||||
tempDatabaseName = (!string.IsNullOrEmpty(builder.InitialCatalog)) ?
|
||||
builder.InitialCatalog : builder.AttachDBFilename;
|
||||
|
||||
// If at this point the dbName remained an empty string then
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Credentials
|
||||
/// </summary>
|
||||
internal CredentialService(ICredentialStore store, StoreConfig config)
|
||||
{
|
||||
this.credStore = store != null ? store : GetStoreForOS(config);
|
||||
credStore = store != null ? store : GetStoreForOS(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
//
|
||||
// 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()
|
||||
{
|
||||
return Create(typeof(ExtensionStore).GetTypeInfo().Assembly.SingleItemAsEnumerable());
|
||||
}
|
||||
|
||||
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,110 @@
|
||||
//
|
||||
// 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.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,113 @@
|
||||
//
|
||||
// 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 Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// A formatting request to process an entire document
|
||||
/// </summary>
|
||||
public class DocumentFormattingRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<DocumentFormattingParams, TextEdit[]> Type =
|
||||
RequestType<DocumentFormattingParams, TextEdit[]>.Create("textDocument/formatting");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A formatting request to process a specific range inside a document
|
||||
/// </summary>
|
||||
public class DocumentRangeFormattingRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<DocumentRangeFormattingParams, TextEdit[]> Type =
|
||||
RequestType<DocumentRangeFormattingParams, TextEdit[]>.Create("textDocument/rangeFormatting");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A formatting request to handle a user typing, giving a chance to update the text based on this
|
||||
/// </summary>
|
||||
public class DocumentOnTypeFormattingRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<DocumentOnTypeFormattingParams, TextEdit[]> Type =
|
||||
RequestType<DocumentOnTypeFormattingParams, TextEdit[]>.Create("textDocument/onTypeFormatting");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Params for the <see cref="DocumentFormattingRequest"/>
|
||||
/// </summary>
|
||||
public class DocumentFormattingParams
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The document to format.
|
||||
/// </summary>
|
||||
public TextDocumentIdentifier TextDocument { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The formatting options
|
||||
/// </summary>
|
||||
public FormattingOptions Options { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Params for the <see cref="DocumentRangeFormattingRequest"/>
|
||||
/// </summary>
|
||||
public class DocumentRangeFormattingParams : DocumentFormattingParams
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The range to format
|
||||
/// </summary>
|
||||
public Range Range { get; set; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Params for the <see cref="DocumentOnTypeFormattingRequest"/>
|
||||
/// </summary>
|
||||
public class DocumentOnTypeFormattingParams : DocumentFormattingParams
|
||||
{
|
||||
/// <summary>
|
||||
/// The position at which this request was sent.
|
||||
|
||||
/// </summary>
|
||||
Position Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The character that has been typed.
|
||||
|
||||
/// </summary>
|
||||
string Ch { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Value-object describing what options formatting should use.
|
||||
/// </summary>
|
||||
public class FormattingOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Size of a tab in spaces
|
||||
/// </summary>
|
||||
public int TabSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Prefer spaces over tabs.
|
||||
/// </summary>
|
||||
public bool InsertSpaces { get; set; }
|
||||
|
||||
// TODO there may be other options passed by VSCode - format is
|
||||
// [key: string]: boolean | number | string;
|
||||
// Determine how these might be passed and add them here
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
public abstract class ASTNodeFormatter
|
||||
{
|
||||
/// <summary>
|
||||
/// Formats the text for a specific node.
|
||||
/// </summary>
|
||||
public abstract void Format();
|
||||
|
||||
internal static LexLocation GetLexLocationForNode(SqlCodeObject obj)
|
||||
{
|
||||
return obj.Position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
//
|
||||
// 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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
internal abstract class ASTNodeFormatterT<T> : ASTNodeFormatter where T : SqlCodeObject
|
||||
{
|
||||
protected FormatterVisitor Visitor { get; private set; }
|
||||
protected T CodeObject { get; private set; }
|
||||
|
||||
public ASTNodeFormatterT(FormatterVisitor visitor, T codeObject)
|
||||
{
|
||||
Validate.IsNotNull(nameof(visitor), visitor);
|
||||
Validate.IsNotNull(nameof(codeObject), codeObject);
|
||||
|
||||
Visitor = visitor;
|
||||
CodeObject = codeObject;
|
||||
}
|
||||
|
||||
protected TokenManager TokenManager
|
||||
{
|
||||
get { return Visitor.Context.Script.TokenManager; }
|
||||
}
|
||||
|
||||
protected FormatOptions FormatOptions
|
||||
{
|
||||
get { return Visitor.Context.FormatOptions; }
|
||||
}
|
||||
|
||||
internal virtual void ProcessChild(SqlCodeObject child)
|
||||
{
|
||||
Validate.IsNotNull(nameof(child), child);
|
||||
child.Accept(Visitor);
|
||||
}
|
||||
|
||||
protected void IncrementIndentLevel()
|
||||
{
|
||||
Visitor.Context.IncrementIndentLevel();
|
||||
}
|
||||
|
||||
protected void DecrementIndentLevel()
|
||||
{
|
||||
Visitor.Context.DecrementIndentLevel();
|
||||
}
|
||||
|
||||
protected void ProcessTokenRange(int startTokenNumber, int endTokenNumber)
|
||||
{
|
||||
Visitor.Context.ProcessTokenRange(startTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
protected void ProcessTokenRangeEnsuringOneNewLineMinumum(int startindex, int endIndex)
|
||||
{
|
||||
ProcessAndNormalizeWhitespaceRange(startindex, endIndex, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
|
||||
protected void ProcessAndNormalizeWhitespaceRange(int startindex, int endIndex, NormalizeWhitespace normalizer)
|
||||
{
|
||||
ProcessAndNormalizeTokenRange(startindex, endIndex, normalizer, true);
|
||||
}
|
||||
|
||||
|
||||
protected void ProcessAndNormalizeTokenRange(int startindex, int endIndex,
|
||||
NormalizeWhitespace normalizer, bool areAllTokensWhitespace)
|
||||
{
|
||||
for (int i = startindex; i < endIndex; i++)
|
||||
{
|
||||
ProcessTokenAndNormalize(i, normalizer, areAllTokensWhitespace);
|
||||
}
|
||||
}
|
||||
|
||||
protected void ProcessTokenAndNormalize(int tokenIndex, NormalizeWhitespace normalizeFunction, bool areAllTokensWhitespace = true)
|
||||
{
|
||||
TokenData iTokenData = GetTokenData(tokenIndex);
|
||||
|
||||
if (areAllTokensWhitespace)
|
||||
{
|
||||
DebugAssertTokenIsWhitespaceOrComment(iTokenData, tokenIndex);
|
||||
}
|
||||
normalizeFunction = normalizeFunction ?? FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum;
|
||||
SimpleProcessToken(tokenIndex, normalizeFunction);
|
||||
}
|
||||
|
||||
protected void DebugAssertTokenIsWhitespaceOrComment(TokenData td, int tokenIndex)
|
||||
{
|
||||
Debug.Assert(TokenManager.IsTokenComment(td.TokenId) || IsTokenWhitespace(td), string.Format(CultureInfo.CurrentCulture,
|
||||
"Unexpected token \"{0}\", expected whitespace or comment.", GetTextForCurrentToken(tokenIndex))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logical aliases for ProcessTokenRange that indicates the starting region is to be analyzed
|
||||
/// </summary>
|
||||
internal virtual void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
ProcessTokenRange(startTokenNumber, firstChildStartTokenNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logical aliases for ProcessTokenRange that indicates the end region is to be analyzed
|
||||
/// </summary>
|
||||
internal virtual void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
ProcessTokenRange(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
internal virtual void ProcessInterChildRegion(SqlCodeObject lastChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(lastChild), lastChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
int lastChildEnd = lastChild.Position.endTokenNumber;
|
||||
int nextChildStart = nextChild.Position.startTokenNumber;
|
||||
|
||||
ProcessTokenRange(lastChildEnd, nextChildStart);
|
||||
}
|
||||
|
||||
public override void Format()
|
||||
{
|
||||
LexLocation loc = GetLexLocationForNode(CodeObject);
|
||||
|
||||
SqlCodeObject firstChild = CodeObject.Children.FirstOrDefault();
|
||||
if (firstChild != null)
|
||||
{
|
||||
//
|
||||
// format the text from the start of the object to the start of it's first child
|
||||
//
|
||||
LexLocation firstChildStart = GetLexLocationForNode(firstChild);
|
||||
ProcessPrefixRegion(loc.startTokenNumber, firstChildStart.startTokenNumber);
|
||||
|
||||
//LexLocation lastChildLexLocation = null;
|
||||
SqlCodeObject previousChild = null;
|
||||
foreach (SqlCodeObject child in CodeObject.Children)
|
||||
{
|
||||
//
|
||||
// format text between the last child's end & current child's start
|
||||
//
|
||||
if (previousChild != null)
|
||||
{
|
||||
//ProcessInterChildRegion(lastChildLexLocation.endTokenNumber, childLexLocation.startTokenNumber);
|
||||
ProcessInterChildRegion(previousChild, child);
|
||||
}
|
||||
|
||||
//
|
||||
// format text of the the current child
|
||||
//
|
||||
ProcessChild(child);
|
||||
previousChild = child;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// format text from end of last child to end of object.
|
||||
//
|
||||
Debug.Assert(previousChild != null, "last child is null. Need to write code to deal with this case");
|
||||
ProcessSuffixRegion(previousChild.Position.endTokenNumber, loc.endTokenNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no children
|
||||
ProcessTokenRange(loc.startTokenNumber, loc.endTokenNumber);
|
||||
}
|
||||
}
|
||||
|
||||
protected void SimpleProcessToken(int currentToken, NormalizeWhitespace normalizeFunction)
|
||||
{
|
||||
TokenData t = GetTokenData(currentToken);
|
||||
if (IsTokenWhitespace(t))
|
||||
{
|
||||
ProcessWhitepace(currentToken, normalizeFunction, t);
|
||||
}
|
||||
else if (t.TokenId == FormatterTokens.LEX_END_OF_LINE_COMMENT)
|
||||
{
|
||||
ProcessEndOfLine(currentToken, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessTokenRange(currentToken, currentToken + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessWhitepace(int currentToken, NormalizeWhitespace normalizeFunction, TokenData token)
|
||||
{
|
||||
string originalWhiteSpace = GetTextForCurrentToken(currentToken);
|
||||
if (HasPreviousToken(currentToken))
|
||||
{
|
||||
TokenData previousToken = PreviousTokenData(currentToken);
|
||||
if (previousToken.TokenId == FormatterTokens.LEX_END_OF_LINE_COMMENT)
|
||||
{
|
||||
if (originalWhiteSpace.StartsWith("\n", StringComparison.OrdinalIgnoreCase)
|
||||
&& RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
// Replace \n with \r\n on Windows platforms
|
||||
originalWhiteSpace = Environment.NewLine + originalWhiteSpace.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string newWhiteSpace = normalizeFunction(originalWhiteSpace, Visitor.Context);
|
||||
|
||||
AddReplacement(new Replacement(token.StartIndex, GetTextForCurrentToken(currentToken), newWhiteSpace));
|
||||
}
|
||||
|
||||
protected string GetTextForCurrentToken(int currentToken)
|
||||
{
|
||||
return Visitor.Context.GetTokenRangeAsOriginalString(currentToken, currentToken + 1);
|
||||
}
|
||||
|
||||
protected string GetTokenRangeAsOriginalString(int startTokenNumber, int endTokenNumber)
|
||||
{
|
||||
return Visitor.Context.GetTokenRangeAsOriginalString(startTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
private void ProcessEndOfLine(int currentToken, TokenData t)
|
||||
{
|
||||
//
|
||||
// the new line character is split over the LEX_END_OF_LINE_COMMENT token and a following whitespace token.
|
||||
// we deal with that here.
|
||||
//
|
||||
string comment = GetTextForCurrentToken(currentToken);
|
||||
if (comment.EndsWith("\r", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
AddReplacement(new Replacement(t.StartIndex, comment, comment.Substring(0, comment.Length - 1)));
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsTokenWithIdWhitespace(int tokenId)
|
||||
{
|
||||
if (HasToken(tokenId))
|
||||
{
|
||||
return TokenManager.IsTokenWhitespace(TokenManager.TokenList[tokenId].TokenId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool IsTokenWhitespace(TokenData tokenData)
|
||||
{
|
||||
return TokenManager.IsTokenWhitespace(tokenData.TokenId);
|
||||
}
|
||||
|
||||
|
||||
protected TokenData GetTokenData(int currentToken)
|
||||
{
|
||||
if (HasToken(currentToken))
|
||||
{
|
||||
return TokenManager.TokenList[currentToken];
|
||||
}
|
||||
return default(TokenData);
|
||||
}
|
||||
|
||||
protected TokenData PreviousTokenData(int currentToken)
|
||||
{
|
||||
if (HasPreviousToken(currentToken))
|
||||
{
|
||||
return TokenManager.TokenList[currentToken - 1];
|
||||
}
|
||||
return default(TokenData);
|
||||
}
|
||||
|
||||
protected TokenData NextTokenData(int currentToken)
|
||||
{
|
||||
if (HasToken(currentToken))
|
||||
{
|
||||
return TokenManager.TokenList[currentToken + 1];
|
||||
}
|
||||
return default(TokenData);
|
||||
}
|
||||
|
||||
protected bool HasPreviousToken(int currentToken)
|
||||
{
|
||||
return HasToken(currentToken - 1);
|
||||
}
|
||||
|
||||
protected bool HasToken(int tokenIndex)
|
||||
{
|
||||
return tokenIndex >= 0 && tokenIndex < TokenManager.TokenList.Count;
|
||||
}
|
||||
|
||||
protected void AddReplacement(Replacement replacement)
|
||||
{
|
||||
Visitor.Context.Replacements.Add(replacement);
|
||||
}
|
||||
|
||||
protected void AddReplacement(int startIndex, string oldValue, string newValue)
|
||||
{
|
||||
AddReplacement(new Replacement(startIndex, oldValue, newValue));
|
||||
}
|
||||
|
||||
protected void AddIndentedNewLineReplacement(int startIndex)
|
||||
{
|
||||
AddReplacement(new Replacement(startIndex, string.Empty, Environment.NewLine + Visitor.Context.GetIndentString()));
|
||||
}
|
||||
|
||||
protected string GetIndentString()
|
||||
{
|
||||
return Visitor.Context.GetIndentString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an expected token
|
||||
/// </summary>
|
||||
/// <param name="currentIndex">Current index to start the search at</param>
|
||||
/// <param name="id">ID defining the type of token being looked for - e.g. parenthesis, INSERT</param>
|
||||
protected int FindTokenWithId(int currentIndex, int id)
|
||||
{
|
||||
TokenData td = GetTokenData(currentIndex);
|
||||
while (td.TokenId != id && currentIndex < CodeObject.Position.endTokenNumber)
|
||||
{
|
||||
DebugAssertTokenIsWhitespaceOrComment(td, currentIndex);
|
||||
++currentIndex;
|
||||
td = GetTokenData(currentIndex);
|
||||
}
|
||||
Debug.Assert(currentIndex < CodeObject.Position.endTokenNumber, "No token with ID" + id + " found in the columns definition.");
|
||||
return currentIndex;
|
||||
}
|
||||
|
||||
internal delegate string NormalizeWhitespace(string original, FormatContext context);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
internal abstract class ASTNodeFormatterFactory
|
||||
{
|
||||
public abstract Type SupportedNodeType { get; }
|
||||
public abstract ASTNodeFormatter Create(FormatterVisitor visitor, SqlCodeObject codeObject);
|
||||
}
|
||||
|
||||
internal abstract class ASTNodeFormatterFactoryT<T> : ASTNodeFormatterFactory
|
||||
where T : SqlCodeObject
|
||||
{
|
||||
public override Type SupportedNodeType
|
||||
{
|
||||
get
|
||||
{
|
||||
return typeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
public override ASTNodeFormatter Create(FormatterVisitor visitor, SqlCodeObject codeObject)
|
||||
{
|
||||
Validate.IsNotNull(nameof(visitor), visitor);
|
||||
Validate.IsNotNull(nameof(codeObject), codeObject);
|
||||
|
||||
return DoCreate(visitor, codeObject as T);
|
||||
}
|
||||
|
||||
protected abstract ASTNodeFormatter DoCreate(FormatterVisitor visitor, T codeObject);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
//
|
||||
// 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 Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
internal class CommaSeparatedListFormatter : ASTNodeFormatterT<SqlCodeObject>
|
||||
{
|
||||
private bool PlaceEachElementOnNewLine { get; set; }
|
||||
|
||||
internal CommaSeparatedListFormatter(FormatterVisitor visitor, SqlCodeObject codeObject, bool placeEachElementOnNewLine)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
PlaceEachElementOnNewLine = placeEachElementOnNewLine;
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
IncrementIndentLevel();
|
||||
|
||||
NormalizeWhitespace f = FormatterUtilities.NormalizeNewLinesOrCondenseToOneSpace;
|
||||
if (PlaceEachElementOnNewLine)
|
||||
{
|
||||
f = FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum;
|
||||
}
|
||||
|
||||
for (int i = startTokenNumber; i < firstChildStartTokenNumber; i++)
|
||||
{
|
||||
SimpleProcessToken(i, f);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
DecrementIndentLevel();
|
||||
ProcessTokenRange(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
int start = previousChild.Position.endTokenNumber;
|
||||
int end = nextChild.Position.startTokenNumber;
|
||||
|
||||
bool foundNonWhitespaceTokenBeforeComma = false;
|
||||
int commaToken = -1;
|
||||
|
||||
for (int i = start; i < end && HasToken(i); i++)
|
||||
{
|
||||
TokenData td = GetTokenData(i);
|
||||
if (td.TokenId == 44)
|
||||
{
|
||||
commaToken = i;
|
||||
break;
|
||||
}
|
||||
else if (IsTokenWhitespace(td))
|
||||
{
|
||||
foundNonWhitespaceTokenBeforeComma = true;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(commaToken > -1, "No comma separating the children.");
|
||||
|
||||
if (foundNonWhitespaceTokenBeforeComma)
|
||||
{
|
||||
ProcessTokenRange(start, commaToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
for (int i = start; i < commaToken && HasToken(i); i++)
|
||||
{
|
||||
TokenData td = GetTokenData(i);
|
||||
if (!IsTokenWhitespace(td))
|
||||
{
|
||||
Debug.Fail("unexpected token type of " + td.TokenId);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// strip whitespace before comma
|
||||
for (int i = start; i < commaToken; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.StripAllWhitespace);
|
||||
}
|
||||
}
|
||||
|
||||
// include comma after each element?
|
||||
if (!FormatOptions.PlaceCommasBeforeNextStatement)
|
||||
{
|
||||
ProcessTokenRange(commaToken, commaToken + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
TokenData token = GetTokenData(commaToken);
|
||||
AddReplacement(new Replacement(token.StartIndex, ",", ""));
|
||||
}
|
||||
|
||||
// special case if there is no white space between comma token and end of region
|
||||
if (commaToken + 1 == end)
|
||||
{
|
||||
string newValue = PlaceEachElementOnNewLine ? Environment.NewLine + GetIndentString() : " ";
|
||||
AddReplacement(new Replacement(
|
||||
GetTokenData(end).StartIndex,
|
||||
string.Empty,
|
||||
newValue
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
NormalizeWhitespace f = FormatterUtilities.NormalizeNewLinesOrCondenseToOneSpace;
|
||||
if (PlaceEachElementOnNewLine)
|
||||
{
|
||||
f = FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum;
|
||||
}
|
||||
|
||||
for (int i = commaToken + 1; i < end; i++)
|
||||
{
|
||||
SimpleProcessToken(i, f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// do we need to place the comma before the next statement in the list?
|
||||
if (FormatOptions.PlaceCommasBeforeNextStatement)
|
||||
{
|
||||
SimpleProcessToken(commaToken, FormatterUtilities.NormalizeNewLinesInWhitespace);
|
||||
TokenData tok = GetTokenData(end);
|
||||
AddReplacement(new Replacement(tok.StartIndex, "", ","));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
internal class FormatContext
|
||||
{
|
||||
private ReplacementQueue replacements = new ReplacementQueue();
|
||||
private string formattedSql;
|
||||
|
||||
internal FormatContext(SqlScript sqlScript, FormatOptions options)
|
||||
{
|
||||
FormatOptions = options;
|
||||
Script = sqlScript;
|
||||
LoadKeywordIdentifiers();
|
||||
}
|
||||
|
||||
internal SqlScript Script { get; private set; }
|
||||
internal FormatOptions FormatOptions { get; set; }
|
||||
internal int IndentLevel { get; set; }
|
||||
internal HashSet<int> KeywordIdentifiers { get; set; }
|
||||
|
||||
private void LoadKeywordIdentifiers()
|
||||
{
|
||||
KeywordIdentifiers = new HashSet<int>();
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_FROM);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_SELECT);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_TABLE);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_CREATE);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_USEDB);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_NOT);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_NULL);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_IDENTITY);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_ORDER);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_BY);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_DESC);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_ASC);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_GROUP);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_WHERE);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_JOIN);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_ON);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_UNION);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_ALL);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_EXCEPT);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_INTERSECT);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_INTO);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_DEFAULT);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_WITH);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_AS);
|
||||
KeywordIdentifiers.Add(FormatterTokens.LEX_BATCH_SEPERATOR);
|
||||
KeywordIdentifiers.Add(FormatterTokens.TOKEN_IS);
|
||||
}
|
||||
|
||||
public string FormattedSql
|
||||
{
|
||||
get
|
||||
{
|
||||
if (formattedSql == null)
|
||||
{
|
||||
DoFormatSql();
|
||||
}
|
||||
return formattedSql;
|
||||
}
|
||||
}
|
||||
|
||||
private void DoFormatSql()
|
||||
{
|
||||
StringBuilder code = new StringBuilder(Script.Sql);
|
||||
foreach (Replacement r in Replacements)
|
||||
{
|
||||
r.Apply((int position, int length, string formattedText) =>
|
||||
{
|
||||
if (length > 0)
|
||||
{
|
||||
if (formattedText.Length > 0)
|
||||
{
|
||||
code.Remove(position, length);
|
||||
code.Insert(position, formattedText);
|
||||
}
|
||||
else
|
||||
{
|
||||
code.Remove(position, length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (formattedText.Length > 0)
|
||||
{
|
||||
code.Insert(position, formattedText);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException(SR.ErrorEmptyStringReplacement);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
formattedSql = code.ToString();
|
||||
}
|
||||
|
||||
public ReplacementQueue Replacements
|
||||
{
|
||||
get
|
||||
{
|
||||
return replacements;
|
||||
}
|
||||
}
|
||||
|
||||
internal void IncrementIndentLevel()
|
||||
{
|
||||
IndentLevel++;
|
||||
}
|
||||
|
||||
internal void DecrementIndentLevel()
|
||||
{
|
||||
if (IndentLevel == 0)
|
||||
{
|
||||
throw new FormatFailedException("can't decrement indent level. It is already 0.");
|
||||
}
|
||||
IndentLevel--;
|
||||
}
|
||||
|
||||
public string GetIndentString()
|
||||
{
|
||||
if (FormatOptions.UseTabs)
|
||||
{
|
||||
return new string('\t', IndentLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new string(' ', IndentLevel * FormatOptions.SpacesPerIndent);
|
||||
}
|
||||
}
|
||||
|
||||
internal string GetTokenRangeAsOriginalString(int startTokenNumber, int endTokenNumber)
|
||||
{
|
||||
string sql = string.Empty;
|
||||
if (endTokenNumber > startTokenNumber && startTokenNumber > -1 && endTokenNumber > -1)
|
||||
{
|
||||
sql = Script.TokenManager.GetText(startTokenNumber, endTokenNumber);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will apply any token-level formatting (e.g., uppercase/lowercase of keywords).
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
|
||||
internal void ProcessTokenRange(int startTokenNumber, int endTokenNumber)
|
||||
{
|
||||
|
||||
for (int i = startTokenNumber; i < endTokenNumber; i++)
|
||||
{
|
||||
string sql = GetTokenRangeAsOriginalString(i, i + 1);
|
||||
|
||||
if (IsKeywordToken(Script.TokenManager.TokenList[i].TokenId))
|
||||
{
|
||||
if (FormatOptions.UppercaseKeywords)
|
||||
{
|
||||
TokenData tok = Script.TokenManager.TokenList[i];
|
||||
Replacements.Add(new Replacement(tok.StartIndex, sql, sql.ToUpperInvariant()));
|
||||
sql = sql.ToUpperInvariant();
|
||||
}
|
||||
else if (FormatOptions.LowercaseKeywords)
|
||||
{
|
||||
TokenData tok = Script.TokenManager.TokenList[i];
|
||||
Replacements.Add(new Replacement(tok.StartIndex, sql, sql.ToLowerInvariant()));
|
||||
sql = sql.ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal void AppendTokenRangeAsString(int startTokenNumber, int endTokenNumber)
|
||||
{
|
||||
ProcessTokenRange(startTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
private bool IsKeywordToken(int tokenId)
|
||||
{
|
||||
return KeywordIdentifiers.Contains(tokenId);
|
||||
}
|
||||
|
||||
internal List<PaddedSpaceSeparatedListFormatter.ColumnSpacingFormatDefinition> CurrentColumnSpacingFormatDefinitions { get; set; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
public class FormatFailedException : Exception
|
||||
{
|
||||
public FormatFailedException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public FormatFailedException(string message, Exception exception)
|
||||
: base(message, exception)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public FormatFailedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
//
|
||||
// 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.ComponentModel;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
public enum CasingOptions { None, Uppercase, Lowercase };
|
||||
|
||||
/// <summary>
|
||||
/// The supported options to use when formatting text
|
||||
/// </summary>
|
||||
public class FormatOptions : INotifyPropertyChanged
|
||||
{
|
||||
|
||||
private int spacesPerIndent;
|
||||
private bool useTabs = false;
|
||||
private bool encloseIdentifiersInSquareBrackets;
|
||||
private bool placeCommasBeforeNextStatement;
|
||||
private bool placeEachReferenceOnNewLineInQueryStatements;
|
||||
private CasingOptions keywordCasing;
|
||||
private CasingOptions datatypeCasing;
|
||||
private bool alignColumnDefinitionsInColumns;
|
||||
|
||||
internal FormatOptions()
|
||||
{
|
||||
SpacesPerIndent = 4;
|
||||
UseTabs = false;
|
||||
PlaceCommasBeforeNextStatement = false;
|
||||
EncloseIdentifiersInSquareBrackets = false;
|
||||
PlaceEachReferenceOnNewLineInQueryStatements = false;
|
||||
}
|
||||
|
||||
public int SpacesPerIndent
|
||||
{
|
||||
get { return spacesPerIndent; }
|
||||
set { spacesPerIndent = value;
|
||||
RaisePropertyChanged("SpacesPerIndent"); }
|
||||
}
|
||||
|
||||
public bool UseTabs
|
||||
{
|
||||
get { return useTabs; }
|
||||
set
|
||||
{
|
||||
useTabs = value;
|
||||
// raise UseTabs & UseSpaces property changed events
|
||||
RaisePropertyChanged("UseTabs");
|
||||
RaisePropertyChanged("UseSpaces");
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseSpaces
|
||||
{
|
||||
get { return !UseTabs; }
|
||||
set { UseTabs = !value; }
|
||||
}
|
||||
|
||||
public bool EncloseIdentifiersInSquareBrackets
|
||||
{
|
||||
get { return encloseIdentifiersInSquareBrackets; }
|
||||
set
|
||||
{
|
||||
encloseIdentifiersInSquareBrackets = value;
|
||||
RaisePropertyChanged("EncloseIdentifiersInSquareBrackets");
|
||||
}
|
||||
}
|
||||
|
||||
public bool PlaceCommasBeforeNextStatement
|
||||
{
|
||||
get { return placeCommasBeforeNextStatement; }
|
||||
set
|
||||
{
|
||||
placeCommasBeforeNextStatement = value;
|
||||
RaisePropertyChanged("PlaceCommasBeforeNextStatement");
|
||||
}
|
||||
}
|
||||
|
||||
public bool PlaceEachReferenceOnNewLineInQueryStatements
|
||||
{
|
||||
get { return placeEachReferenceOnNewLineInQueryStatements; }
|
||||
set
|
||||
{
|
||||
placeEachReferenceOnNewLineInQueryStatements = value;
|
||||
RaisePropertyChanged("PlaceEachReferenceOnNewLineInQueryStatements");
|
||||
}
|
||||
}
|
||||
|
||||
public CasingOptions KeywordCasing
|
||||
{
|
||||
get { return keywordCasing; }
|
||||
set
|
||||
{
|
||||
keywordCasing = value;
|
||||
RaisePropertyChanged("KeywordCasing");
|
||||
}
|
||||
}
|
||||
|
||||
public bool UppercaseKeywords
|
||||
{
|
||||
get { return KeywordCasing == CasingOptions.Uppercase; }
|
||||
}
|
||||
public bool LowercaseKeywords
|
||||
{
|
||||
get { return KeywordCasing == CasingOptions.Lowercase; }
|
||||
}
|
||||
|
||||
public bool DoNotFormatKeywords
|
||||
{
|
||||
get { return KeywordCasing == CasingOptions.None; }
|
||||
}
|
||||
|
||||
public CasingOptions DatatypeCasing
|
||||
{
|
||||
get { return datatypeCasing; }
|
||||
set
|
||||
{
|
||||
datatypeCasing = value;
|
||||
RaisePropertyChanged("DatatypeCasing");
|
||||
}
|
||||
}
|
||||
|
||||
public bool UppercaseDataTypes
|
||||
{
|
||||
get { return DatatypeCasing == CasingOptions.Uppercase; }
|
||||
}
|
||||
public bool LowercaseDataTypes
|
||||
{
|
||||
get { return DatatypeCasing == CasingOptions.Lowercase; }
|
||||
}
|
||||
public bool DoNotFormatDataTypes
|
||||
{
|
||||
get { return DatatypeCasing == CasingOptions.None; }
|
||||
}
|
||||
|
||||
public bool AlignColumnDefinitionsInColumns
|
||||
{
|
||||
get { return alignColumnDefinitionsInColumns; }
|
||||
set
|
||||
{
|
||||
alignColumnDefinitionsInColumns = value;
|
||||
RaisePropertyChanged("AlignColumnDefinitionsInColumns");
|
||||
}
|
||||
}
|
||||
|
||||
private void RaisePropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public static void Copy(FormatOptions target, FormatOptions source)
|
||||
{
|
||||
target.AlignColumnDefinitionsInColumns = source.AlignColumnDefinitionsInColumns;
|
||||
target.DatatypeCasing = source.DatatypeCasing;
|
||||
target.EncloseIdentifiersInSquareBrackets = source.EncloseIdentifiersInSquareBrackets;
|
||||
target.KeywordCasing = source.KeywordCasing;
|
||||
target.PlaceCommasBeforeNextStatement = source.PlaceCommasBeforeNextStatement;
|
||||
target.PlaceEachReferenceOnNewLineInQueryStatements = source.PlaceEachReferenceOnNewLineInQueryStatements;
|
||||
target.SpacesPerIndent = source.SpacesPerIndent;
|
||||
target.UseSpaces = source.UseSpaces;
|
||||
target.UseTabs = source.UseTabs;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.ComponentModel;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
/// <summary>
|
||||
/// Dynamically resolves the token IDs to match the values in the enum Microsoft.SqlServer.Management.SqlParser.Parser.Tokens.
|
||||
/// This way, if the values in the enum change but their names remain the same
|
||||
/// (when the Microsoft.SqlServer.Management.SqlParser.Parser.dll adds new tokens to the enum and is rebuilt),
|
||||
/// the new values are retreived at runtime without having to rebuild Microsoft.SqlTools.ServiceLayer.Formatter.dll
|
||||
/// </summary>
|
||||
static class FormatterTokens
|
||||
{
|
||||
private static int ResolveTokenId(string tokenName)
|
||||
{
|
||||
EnumConverter converter = new EnumConverter(typeof(Tokens));
|
||||
return (int)converter.ConvertFromString(tokenName);
|
||||
}
|
||||
|
||||
public static readonly int TOKEN_FOR = ResolveTokenId("TOKEN_FOR");
|
||||
public static readonly int TOKEN_REPLICATION = ResolveTokenId("TOKEN_REPLICATION");
|
||||
public static readonly int TOKEN_ID = ResolveTokenId("TOKEN_ID");
|
||||
public static readonly int LEX_END_OF_LINE_COMMENT = ResolveTokenId("LEX_END_OF_LINE_COMMENT");
|
||||
public static readonly int TOKEN_FROM = ResolveTokenId("TOKEN_FROM");
|
||||
public static readonly int TOKEN_SELECT = ResolveTokenId("TOKEN_SELECT");
|
||||
public static readonly int TOKEN_TABLE = ResolveTokenId("TOKEN_TABLE");
|
||||
public static readonly int TOKEN_USEDB = ResolveTokenId("TOKEN_USEDB");
|
||||
public static readonly int TOKEN_NOT = ResolveTokenId("TOKEN_NOT");
|
||||
public static readonly int TOKEN_NULL = ResolveTokenId("TOKEN_NULL");
|
||||
public static readonly int TOKEN_IDENTITY = ResolveTokenId("TOKEN_IDENTITY");
|
||||
public static readonly int TOKEN_ORDER = ResolveTokenId("TOKEN_ORDER");
|
||||
public static readonly int TOKEN_BY = ResolveTokenId("TOKEN_BY");
|
||||
public static readonly int TOKEN_DESC = ResolveTokenId("TOKEN_DESC");
|
||||
public static readonly int TOKEN_ASC = ResolveTokenId("TOKEN_ASC");
|
||||
public static readonly int TOKEN_GROUP = ResolveTokenId("TOKEN_GROUP");
|
||||
public static readonly int TOKEN_WHERE = ResolveTokenId("TOKEN_WHERE");
|
||||
public static readonly int TOKEN_JOIN = ResolveTokenId("TOKEN_JOIN");
|
||||
public static readonly int TOKEN_ON = ResolveTokenId("TOKEN_ON");
|
||||
public static readonly int TOKEN_UNION = ResolveTokenId("TOKEN_UNION");
|
||||
public static readonly int TOKEN_ALL = ResolveTokenId("TOKEN_ALL");
|
||||
public static readonly int TOKEN_EXCEPT = ResolveTokenId("TOKEN_EXCEPT");
|
||||
public static readonly int TOKEN_INTERSECT = ResolveTokenId("TOKEN_INTERSECT");
|
||||
public static readonly int TOKEN_INTO = ResolveTokenId("TOKEN_INTO");
|
||||
public static readonly int TOKEN_DEFAULT = ResolveTokenId("TOKEN_DEFAULT");
|
||||
public static readonly int TOKEN_WITH = ResolveTokenId("TOKEN_WITH");
|
||||
public static readonly int TOKEN_AS = ResolveTokenId("TOKEN_AS");
|
||||
public static readonly int TOKEN_IS = ResolveTokenId("TOKEN_IS");
|
||||
public static readonly int TOKEN_BEGIN_CS = ResolveTokenId("TOKEN_BEGIN_CS");
|
||||
public static readonly int TOKEN_END_CS = ResolveTokenId("TOKEN_END_CS");
|
||||
public static readonly int LEX_BATCH_SEPERATOR = ResolveTokenId("LEX_BATCH_SEPERATOR");
|
||||
public static readonly int TOKEN_CREATE = ResolveTokenId("TOKEN_CREATE");
|
||||
public static readonly int LAST_TOKEN = ResolveTokenId("LAST_TOKEN");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.Text;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
internal static class FormatterUtilities
|
||||
{
|
||||
internal static string StripAllWhitespace(string original, FormatContext context)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
internal static string NormalizeToOneSpace(string original, FormatContext context)
|
||||
{
|
||||
return " ";
|
||||
}
|
||||
|
||||
internal static string NormalizeNewLinesInWhitespace(string original, FormatContext context)
|
||||
{
|
||||
return NormalizeNewLinesInWhitespace(original, context, 0);
|
||||
}
|
||||
|
||||
internal static string NormalizeNewLinesEnsureOneNewLineMinimum(string original, FormatContext context)
|
||||
{
|
||||
return NormalizeNewLinesInWhitespace(original, context, 1);
|
||||
}
|
||||
|
||||
private static string NormalizeNewLinesInWhitespace(string original, FormatContext context, int minimumNewLines)
|
||||
{
|
||||
return NormalizeNewLinesInWhitespace(original, context, 1, () => { return original; });
|
||||
}
|
||||
|
||||
internal static string NormalizeNewLinesOrCondenseToOneSpace(string original, FormatContext context)
|
||||
{
|
||||
return NormalizeNewLinesOrCondenseToNSpaces(original, context, 1);
|
||||
}
|
||||
|
||||
internal static string NormalizeNewLinesOrCondenseToNSpaces(string original, FormatContext context, int nSpaces)
|
||||
{
|
||||
return NormalizeNewLinesInWhitespace(original, context, 0, () => { return new String(' ', nSpaces); });
|
||||
}
|
||||
|
||||
private static string NormalizeNewLinesInWhitespace(string original, FormatContext context, int minimumNewLines, Func<String> noNewLinesProcessor)
|
||||
{
|
||||
int nNewLines = 0;
|
||||
int idx = original.IndexOf(Environment.NewLine, StringComparison.OrdinalIgnoreCase);
|
||||
while (idx > -1)
|
||||
{
|
||||
++nNewLines;
|
||||
idx = original.IndexOf(Environment.NewLine, idx + 1, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
nNewLines = Math.Max(minimumNewLines, nNewLines);
|
||||
for (int i = 0; i < nNewLines; i++)
|
||||
{
|
||||
sb.Append(Environment.NewLine);
|
||||
}
|
||||
sb.Append(context.GetIndentString());
|
||||
|
||||
if (nNewLines > 0)
|
||||
{
|
||||
return sb.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return noNewLinesProcessor();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// 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.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for our formatter implementation, via the <see cref="Format(string, FormatOptions, bool, Replacement.OnReplace)"/> method.
|
||||
/// This converts a text string into a parsed AST using the Intellisense parser.
|
||||
/// It then uses the Visitor pattern to find each element in the tree and determine if any edits are needed based on
|
||||
/// All edits are applied after the entire AST has been visited using an algorithm that keeps track of index changes caused by previous updates. This allows
|
||||
/// us to apply multiple edits to a text string in one sweep.
|
||||
///
|
||||
/// A note on the <see cref="SqlCodeObjectVisitor"/> implementation: All of the override nodes in the Intellisense AST are defined here, and routed to the Format method which looks up a matching
|
||||
/// formatter to handle them. Any entry not explicitly formatted will use the no-op formatter which passes through the text unchanged.
|
||||
/// </summary>
|
||||
internal partial class FormatterVisitor : SqlCodeObjectVisitor
|
||||
{
|
||||
private readonly IMultiServiceProvider serviceProvider;
|
||||
|
||||
public FormatterVisitor(FormatContext context, IMultiServiceProvider serviceProvider)
|
||||
: base()
|
||||
{
|
||||
Context = context;
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
private void Format<T>(T codeObject) where T : SqlCodeObject
|
||||
{
|
||||
ASTNodeFormatter f = GetFormatter(codeObject);
|
||||
f.Format();
|
||||
}
|
||||
|
||||
private ASTNodeFormatter GetFormatter<T>(T codeObject) where T:SqlCodeObject
|
||||
{
|
||||
Type astType = typeof(T);
|
||||
ASTNodeFormatter formatter;
|
||||
|
||||
var formatterFactory = serviceProvider.GetServices<ASTNodeFormatterFactory>().FirstOrDefault(f => astType.Equals(f.SupportedNodeType));
|
||||
if (formatterFactory != null)
|
||||
{
|
||||
formatter = formatterFactory.Create(this, codeObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
formatter = new NoOpFormatter(this, codeObject);
|
||||
}
|
||||
|
||||
return formatter;
|
||||
}
|
||||
|
||||
public FormatContext Context { get; private set; }
|
||||
|
||||
public void VerifyFormat()
|
||||
{
|
||||
ParseResult result = Parser.Parse(Context.FormattedSql);
|
||||
SqlScript newScript = result.Script;
|
||||
VerifyTokenStreamsOnlyDifferByWhitespace(Context.Script, newScript);
|
||||
}
|
||||
|
||||
internal static bool IsTokenWhitespaceOrComma(SqlScript script, int tokenIndex)
|
||||
{
|
||||
int tokenId = script.TokenManager.TokenList[tokenIndex].TokenId;
|
||||
return script.TokenManager.IsTokenWhitespace(tokenId) || (tokenId == 44);
|
||||
}
|
||||
|
||||
internal static bool IsTokenWhitespaceOrComment(SqlScript script, int tokenIndex)
|
||||
{
|
||||
int tokenId = script.TokenManager.TokenList[tokenIndex].TokenId;
|
||||
|
||||
return script.TokenManager.IsTokenWhitespace(tokenId) || script.TokenManager.IsTokenComment(tokenId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the token streams of two SqlScript objects differ only by whitespace tokens or
|
||||
/// by the relative positioning of commas and comments. The important rule enforced is that there are
|
||||
/// no changes in relative positioning which involve tokens other than commas, comments or whitespaces.
|
||||
/// </summary>
|
||||
/// <param name="script1">SQL script containing the first token stream.</param>
|
||||
/// <param name="script2">SQL script containing the second token stream.</param>
|
||||
public static void VerifyTokenStreamsOnlyDifferByWhitespace(SqlScript script1, SqlScript script2)
|
||||
{
|
||||
// We break down the relative positioning problem into assuring that the token streams have identical ids
|
||||
// both when we ignore whitespaces and commas as well as when we ignore whitespaces and comments
|
||||
VerifyTokenStreamsOnlyDifferBy(script1, script2, IsTokenWhitespaceOrComma);
|
||||
VerifyTokenStreamsOnlyDifferBy(script1, script2, IsTokenWhitespaceOrComment);
|
||||
}
|
||||
|
||||
internal delegate bool IgnoreToken(SqlScript script, int tokenIndex);
|
||||
|
||||
public static void VerifyTokenStreamsOnlyDifferBy(SqlScript script1, SqlScript script2, IgnoreToken ignoreToken )
|
||||
{
|
||||
int t1 = 0;
|
||||
int t2 = 0;
|
||||
|
||||
while (t1 < script1.TokenManager.Count && t2 < script2.TokenManager.Count)
|
||||
{
|
||||
// advance t1 until it is pointing at a non-whitespace token
|
||||
while (t1 < script1.TokenManager.Count && ignoreToken(script1, t1))
|
||||
{
|
||||
++t1;
|
||||
}
|
||||
|
||||
// advance t2 until it is pointing at a non-whitespace token
|
||||
while (t2 < script2.TokenManager.Count && ignoreToken(script2, t2))
|
||||
{
|
||||
++t2;
|
||||
}
|
||||
|
||||
if (t1 >= script1.TokenManager.Count || t2 >= script2.TokenManager.Count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// TODO: need special logic here to deal with the placement of commas
|
||||
//
|
||||
|
||||
// verify the tokens are equal
|
||||
if (script1.TokenManager.TokenList[t1].TokenId != script2.TokenManager.TokenList[t2].TokenId)
|
||||
{
|
||||
string msg = "The comparison failed between tokens at {0} & {1}. The token IDs were {2} and {3} respectively. Script1 = {4}. Script2 = {5}";
|
||||
msg = string.Format(CultureInfo.CurrentCulture, msg, t1, t2, script1.TokenManager.TokenList[t1].TokenId, script2.TokenManager.TokenList[t2].TokenId, script1.Sql, script2.Sql);
|
||||
throw new FormatFailedException(msg);
|
||||
}
|
||||
|
||||
++t1;
|
||||
++t2;
|
||||
}
|
||||
|
||||
// one of the streams is exhausted, verify that the only tokens left in the other stream are whitespace tokens
|
||||
Debug.Assert(t1 >= script1.TokenManager.Count || t2 >= script2.TokenManager.Count, "expected to be at the end of one of the token's streams");
|
||||
int t = t1;
|
||||
SqlScript s = script1;
|
||||
if (t2 < script2.TokenManager.Count)
|
||||
{
|
||||
Debug.Assert(t1 >= script1.TokenManager.Count, "expected to be at end of script1's token stream");
|
||||
t = t2;
|
||||
s = script2;
|
||||
}
|
||||
|
||||
while (t < s.TokenManager.Count)
|
||||
{
|
||||
if (!ignoreToken(s, t))
|
||||
{
|
||||
string msg = "Unexpected non-whitespace token at index {0}, token ID {1}";
|
||||
msg = string.Format(CultureInfo.CurrentCulture, msg, t, s.TokenManager.TokenList[t].TokenId);
|
||||
throw new FormatFailedException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
//
|
||||
// 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.CodeAnalysis;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
// Any new SqlCodeObject types should have a Visit method added, and this class should then be updated with a matching
|
||||
// Visit implementation that routes to the Format method.
|
||||
[SuppressMessage("Microsoft.Maintainability","CA1506:AvoidExcessiveClassCoupling")]
|
||||
partial class FormatterVisitor : SqlCodeObjectVisitor
|
||||
{
|
||||
public override void Visit(SqlAggregateFunctionCallExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlAllAnyComparisonBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlAllowPageLocksIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlAllowRowLocksIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlAlterFunctionStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlAlterProcedureStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlAlterTriggerStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlAlterViewStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlAssignment codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBackupCertificateStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBackupDatabaseStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBackupLogStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBackupMasterKeyStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBackupServiceMasterKeyStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBackupTableStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBatch codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBetweenBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBinaryBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBinaryFilterExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBinaryQueryExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBinaryScalarExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBooleanFilterExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBreakStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBuiltinScalarFunctionCallExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCastExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlChangeTrackingContext codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCheckConstraint codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlClrAssemblySpecifier codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlClrClassSpecifier codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlClrFunctionBodyDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlClrMethodSpecifier codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCollateScalarExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCollation codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlColumnAssignment codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlColumnDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlColumnIdentity codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlColumnRefExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCommentStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCommonTableExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlComparisonBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCompoundStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCompressionPartitionRange codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlComputedColumnDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlConditionClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlConstraint codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlContinueStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlConvertExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateFunctionStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateIndexStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateLoginFromAsymKeyStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateLoginFromCertificateStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateLoginFromWindowsStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateLoginWithPasswordStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateProcedureStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateRoleStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateSchemaStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateSynonymStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateTableStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateTriggerStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserDefinedDataTypeStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserDefinedTableTypeStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserDefinedTypeStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserFromAsymKeyStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserFromCertificateStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserFromLoginStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserWithImplicitAuthenticationStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserWithoutLoginStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateViewStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCubeGroupByItem codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCursorDeclareStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCursorOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCursorVariableAssignment codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCursorVariableRefExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDataCompressionIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDataType codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDataTypeSpecification codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDBCCStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDdlTriggerDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDefaultConstraint codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDefaultValuesInsertMergeActionSource codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDefaultValuesInsertSource codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDeleteMergeAction codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDeleteSpecification codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDeleteStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDenyStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDerivedTableExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDmlSpecificationTableSource codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDmlTriggerDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropAggregateStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropDatabaseStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropDefaultStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropExistingIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropFunctionStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropLoginStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropProcedureStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropRuleStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropSchemaStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropSynonymStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropTableStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropTriggerStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropTypeStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropUserStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropViewStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlExecuteAsClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlExecuteModuleStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlExistsBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlFillFactorIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlFilterClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlForBrowseClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlForeignKeyConstraint codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlForXmlAutoClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlForXmlClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlForXmlDirective codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlForXmlExplicitClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlForXmlPathClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlForXmlRawClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlFromClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlFullTextBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlFullTextColumn codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlFunctionDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlGlobalScalarVariableRefExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlGrandTotalGroupByItem codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlGrandTotalGroupingSet codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlGrantStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlGroupByClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlGroupBySets codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlGroupingSetItemsCollection codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlHavingClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlIdentifier codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlIdentityFunctionCallExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlIfElseStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlIgnoreDupKeyIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInBooleanExpressionCollectionValue codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInBooleanExpressionQueryValue codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlIndexedColumn codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlIndexHint codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInlineFunctionBodyDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInlineTableRelationalFunctionDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInlineTableVariableDeclaration codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInlineTableVariableDeclareStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInsertMergeAction codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInsertSource codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInsertSpecification codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInsertStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlIntoClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlIsNullBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlLargeDataStorageInformation codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlLikeBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlLiteralExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlLoginPassword codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlMaxDegreeOfParallelismIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlMergeActionClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlMergeSpecification codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlMergeStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlModuleArgument codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlModuleCalledOnNullInputOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlModuleEncryptionOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlModuleExecuteAsOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlModuleOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlModuleRecompileOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlModuleReturnsNullOnNullInputOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlModuleSchemaBindingOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlModuleViewMetadataOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlMultistatementFunctionBodyDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlMultistatementTableRelationalFunctionDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlNotBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlObjectIdentifier codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlObjectReference codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlOnlineIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlOrderByClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlOrderByItem codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlOutputClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlOutputIntoClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlPadIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlParameterDeclaration codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlPivotClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlPivotTableExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlPrimaryKeyConstraint codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlProcedureDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlQualifiedJoinTableExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlQueryExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlQuerySpecification codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlQueryWithClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlRestoreDatabaseStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlRestoreInformationStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlRestoreLogStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlRestoreMasterKeyStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlRestoreServiceMasterKeyStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlRestoreTableStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlReturnStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlRevokeStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlRollupGroupByItem codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlRowConstructorExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlScalarClrFunctionDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlScalarExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlScalarFunctionReturnType codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlScalarRefExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlScalarRelationalFunctionDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlScalarSubQueryExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlScalarVariableAssignment codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlScalarVariableRefExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlScript codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSearchedCaseExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSearchedWhenClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSelectClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSelectIntoClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSelectScalarExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSelectSpecification codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSelectSpecificationInsertSource codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSelectStarExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSelectStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSelectVariableAssignmentExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSetAssignmentStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSetClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSetStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSimpleCaseExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSimpleGroupByItem codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSimpleOrderByClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSimpleOrderByItem codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSimpleWhenClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSortedDataIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSortedDataReorgIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlSortInTempDbIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlStatisticsNoRecomputeIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlStatisticsOnlyIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlStorageSpecification codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableClrFunctionDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableConstructorExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableConstructorInsertSource codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableFunctionReturnType codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableHint codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableRefExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableUdtInstanceMethodExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableValuedFunctionRefExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTableVariableRefExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTargetTableExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTopSpecification codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTriggerAction codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTriggerDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTriggerEvent codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTryCatchStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUdtInstanceDataMemberExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUdtInstanceMethodExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUdtStaticDataMemberExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUdtStaticMethodExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUnaryScalarExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUniqueConstraint codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUnpivotClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUnpivotTableExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUnqualifiedJoinTableExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUpdateBooleanExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUpdateMergeAction codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUpdateSpecification codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUpdateStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUserDefinedScalarFunctionCallExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlUseStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlValuesInsertMergeActionSource codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlVariableColumnAssignment codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlVariableDeclaration codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlVariableDeclareStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlViewDefinition codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlWhereClause codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlWhileStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlXmlNamespacesDeclaration codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlAtTimeZoneExpression codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlBucketCountIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCompressionDelayIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlCreateUserFromExternalProviderStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropSecurityPolicyStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlDropSequenceStatement codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlInlineIndexConstraint codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlModuleNativeCompilationOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlStatisticsIncrementalIndexOption codeObject) { Format(codeObject); }
|
||||
public override void Visit(SqlTemporalPeriodDefinition codeObject) { Format(codeObject); }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 System;
|
||||
using System.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
internal class NoOpFormatter : ASTNodeFormatterT<SqlCodeObject>
|
||||
{
|
||||
public NoOpFormatter(FormatterVisitor visitor, SqlCodeObject codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// 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.Composition;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
internal class PaddedSpaceSeparatedListFormatter : SpaceSeparatedListFormatter
|
||||
{
|
||||
private List<ColumnSpacingFormatDefinition> ColumnSpacingDefinitions { get; set; }
|
||||
private int nextColumn = 0;
|
||||
|
||||
|
||||
internal PaddedSpaceSeparatedListFormatter(FormatterVisitor visitor, SqlCodeObject codeObject, List<ColumnSpacingFormatDefinition> spacingDefinitions, bool incrementIndentLevelOnPrefixRegion)
|
||||
: base(visitor, codeObject, incrementIndentLevelOnPrefixRegion)
|
||||
{
|
||||
ColumnSpacingDefinitions = spacingDefinitions;
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
// first, figure out how big to make the pad
|
||||
int padLength = 1;
|
||||
if (ColumnSpacingDefinitions != null && nextColumn < ColumnSpacingDefinitions.Count)
|
||||
{
|
||||
if (previousChild.GetType() == ColumnSpacingDefinitions[nextColumn].PreviousType &&
|
||||
(ColumnSpacingDefinitions[nextColumn].NextType == null || nextChild.GetType() == ColumnSpacingDefinitions[nextColumn].NextType))
|
||||
{
|
||||
string text = previousChild.TokenManager.GetText(previousChild.Position.startTokenNumber, previousChild.Position.endTokenNumber);
|
||||
int stringLength = text.Length;
|
||||
padLength = ColumnSpacingDefinitions[nextColumn].PaddedLength - stringLength;
|
||||
|
||||
Debug.Assert(padLength > 0, "unexpected value for Pad Length");
|
||||
padLength = Math.Max(padLength, 1);
|
||||
|
||||
++nextColumn;
|
||||
}
|
||||
}
|
||||
// next, normalize the tokens
|
||||
int start = previousChild.Position.endTokenNumber;
|
||||
int end = nextChild.Position.startTokenNumber;
|
||||
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
SimpleProcessToken(i, (string original, FormatContext context) => { return FormatterUtilities.NormalizeNewLinesOrCondenseToNSpaces(original, context, padLength); });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class ColumnSpacingFormatDefinition
|
||||
{
|
||||
internal ColumnSpacingFormatDefinition(Type previousType, Type nextType, int padLength)
|
||||
{
|
||||
PreviousType = previousType;
|
||||
NextType = nextType;
|
||||
PaddedLength = padLength;
|
||||
}
|
||||
|
||||
internal Type PreviousType { get; private set; }
|
||||
internal Type NextType { get; private set; }
|
||||
internal int PaddedLength { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a string editing action which requests that a particular
|
||||
/// substring found at a given location be replaced by another string
|
||||
/// </summary>
|
||||
public class Replacement
|
||||
{
|
||||
public Replacement(int startIndex, string oldValue, string newValue)
|
||||
{
|
||||
StartIndex = startIndex;
|
||||
OldValue = oldValue;
|
||||
NewValue = newValue;
|
||||
}
|
||||
|
||||
public int StartIndex { get; private set; }
|
||||
public string OldValue { get; private set; }
|
||||
public string NewValue { get; private set; }
|
||||
|
||||
public int EndIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return StartIndex + OldValue.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the replacement will have any effect.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal bool IsIdentity()
|
||||
{
|
||||
return OldValue.Equals(NewValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reports the relative change in text length (number of characters)
|
||||
/// between the initial and the formatted code introduced by this
|
||||
/// particular replacement.
|
||||
/// </summary>
|
||||
public int InducedOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
return NewValue.Length - OldValue.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replacements will often change the length of the code, making
|
||||
/// indexing relative to the original text ambiguous. The CumulatedOffset
|
||||
/// can be used to adjust the relative indexing between the original and the
|
||||
/// edited text as perceived at the start of this replacement and help
|
||||
/// compensate for the difference.
|
||||
/// </summary>
|
||||
public int CumulativeOffset { set; private get; }
|
||||
|
||||
/// <summary>
|
||||
/// A delegate responsible for applying the replacement. Each application assumes
|
||||
/// nothing about other replacements which might have taken place before or which
|
||||
/// might take place after the current one.
|
||||
/// </summary>
|
||||
/// <param name="pos">Position of the begining of the replacement relative to the beginig of the character stream.</param>
|
||||
/// <param name="len">The number of consecutive characters which are to be replaced.</param>
|
||||
/// <param name="with">The characters which are to replace the old ones. Note that the length of this string might be greater or smaller than the number of replaced characters.</param>
|
||||
public delegate void OnReplace(int pos, int len, string with);
|
||||
|
||||
/// <summary>
|
||||
/// Applies a replacement action according to a given strategy defined by the delegate procedure.
|
||||
/// </summary>
|
||||
/// <param name="replace">This delegate function implements the strategy for applying the replacement.</param>
|
||||
public void Apply(OnReplace replace)
|
||||
{
|
||||
replace(StartIndex + CumulativeOffset, OldValue.Length, NewValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// 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.Collections;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
internal class ReplacementQueue : IEnumerable
|
||||
{
|
||||
internal int offset = 0;
|
||||
|
||||
private Queue<Replacement> Replacements { get; set; }
|
||||
|
||||
public ReplacementQueue()
|
||||
{
|
||||
Replacements = new Queue<Replacement>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a replace action to the queue and adjusts its absolute
|
||||
/// offset to reflect the global indexing after applying the replacements
|
||||
/// in the queue.
|
||||
///
|
||||
/// NOTE: The method assumes the replacements occur in front-to-back order
|
||||
/// and that they never overlap.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="r">The latest replacement to be added to the queue.</param>
|
||||
public void Add(Replacement r)
|
||||
{
|
||||
if (!r.IsIdentity())
|
||||
{
|
||||
r.CumulativeOffset = offset;
|
||||
Replacements.Enqueue(r);
|
||||
offset += r.InducedOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return Replacements.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
internal class SpaceSeparatedListFormatter : WhiteSpaceSeparatedListFormatter
|
||||
{
|
||||
internal SpaceSeparatedListFormatter(FormatterVisitor visitor, SqlCodeObject codeObject, bool incrementIndentLevelOnPrefixRegion)
|
||||
: base(visitor, codeObject, incrementIndentLevelOnPrefixRegion)
|
||||
{
|
||||
}
|
||||
|
||||
internal override string FormatWhitespace(string original, FormatContext context)
|
||||
{
|
||||
return FormatterUtilities.NormalizeNewLinesOrCondenseToOneSpace(original, context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class NewLineSeparatedListFormatter : WhiteSpaceSeparatedListFormatter
|
||||
{
|
||||
public NewLineSeparatedListFormatter(FormatterVisitor visitor, SqlCodeObject codeObject, bool incrementIndentLevelOnPrefixRegion)
|
||||
: base(visitor, codeObject, incrementIndentLevelOnPrefixRegion)
|
||||
{
|
||||
}
|
||||
|
||||
internal override string FormatWhitespace(string original, FormatContext context)
|
||||
{
|
||||
return FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum(original, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlBatchFormatterFactory : ASTNodeFormatterFactoryT<SqlBatch>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlBatch codeObject)
|
||||
{
|
||||
return new SqlBatchFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
class SqlBatchFormatter : NewLineSeparatedListFormatter
|
||||
{
|
||||
public SqlBatchFormatter(FormatterVisitor visitor, SqlCodeObject codeObject)
|
||||
:base(visitor, codeObject, false)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
for (int i = startTokenNumber; i < firstChildStartTokenNumber; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// 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.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlBinaryBooleanExpressionFormatterFactory : ASTNodeFormatterFactoryT<SqlBinaryBooleanExpression>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlBinaryBooleanExpression codeObject)
|
||||
{
|
||||
return new SqlBinaryBooleanExpressionFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlBinaryBooleanExpressionFormatter : ASTNodeFormatterT<SqlBinaryBooleanExpression>
|
||||
{
|
||||
SpaceSeparatedListFormatter SpaceSeparatedListFormatter { get; set; }
|
||||
|
||||
internal SqlBinaryBooleanExpressionFormatter(FormatterVisitor visitor, SqlBinaryBooleanExpression codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
SpaceSeparatedListFormatter = new SpaceSeparatedListFormatter(visitor, codeObject, true);
|
||||
}
|
||||
|
||||
internal override void ProcessChild(SqlCodeObject child)
|
||||
{
|
||||
Validate.IsNotNull(nameof(child), child);
|
||||
SpaceSeparatedListFormatter.ProcessChild(child);
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
SpaceSeparatedListFormatter.ProcessPrefixRegion(startTokenNumber, firstChildStartTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
SpaceSeparatedListFormatter.ProcessSuffixRegion(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
SpaceSeparatedListFormatter.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// 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.Composition;
|
||||
using System.Diagnostics;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlBinaryQueryExpressionFormatterFactory : ASTNodeFormatterFactoryT<SqlBinaryQueryExpression>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlBinaryQueryExpression codeObject)
|
||||
{
|
||||
return new SqlBinaryQueryExpressionFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
class SqlBinaryQueryExpressionFormatter : ASTNodeFormatterT<SqlBinaryQueryExpression>
|
||||
{
|
||||
|
||||
internal SqlBinaryQueryExpressionFormatter(FormatterVisitor visitor, SqlBinaryQueryExpression codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
if (CodeObject.Left is SqlQuerySpecification)
|
||||
{
|
||||
IncrementIndentLevel();
|
||||
}
|
||||
|
||||
// if the start token is not a whitespace, we need to insert the indent string
|
||||
TokenData td = GetTokenData(startTokenNumber);
|
||||
if (!IsTokenWhitespace(td))
|
||||
{
|
||||
string newWhiteSpace = GetIndentString();
|
||||
AddReplacement(new Replacement(td.StartIndex, string.Empty, newWhiteSpace));
|
||||
}
|
||||
|
||||
for (int i = startTokenNumber; i < firstChildStartTokenNumber; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
|
||||
if (firstChildStartTokenNumber - 1 >= startTokenNumber)
|
||||
{
|
||||
IndentChild(firstChildStartTokenNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private void IndentChild(int firstChildStartTokenNumber)
|
||||
{
|
||||
string newWhiteSpace = GetIndentString();
|
||||
|
||||
if (!IsTokenWhitespace(PreviousTokenData(firstChildStartTokenNumber)))
|
||||
{
|
||||
newWhiteSpace = Environment.NewLine + newWhiteSpace;
|
||||
}
|
||||
|
||||
TokenData td = GetTokenData(firstChildStartTokenNumber);
|
||||
AddReplacement(td.StartIndex, string.Empty, newWhiteSpace);
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
// find the potition of the operator token:
|
||||
// operatorTokenNumber
|
||||
#region FindOperator
|
||||
|
||||
// look for the expression type based on the operator and determine its type and position
|
||||
int binaryOperatorTokenID = FormatterTokens.LAST_TOKEN;
|
||||
bool foundOperator = false;
|
||||
int operatorTokenNumber = nextChild.Position.startTokenNumber;
|
||||
for (int i = previousChild.Position.endTokenNumber; !foundOperator && i < nextChild.Position.startTokenNumber; i++)
|
||||
{
|
||||
TokenData td = GetTokenData(i);
|
||||
if ( td.TokenId == FormatterTokens.TOKEN_UNION ||
|
||||
td.TokenId == FormatterTokens.TOKEN_INTERSECT ||
|
||||
td.TokenId == FormatterTokens.TOKEN_EXCEPT )
|
||||
{
|
||||
foundOperator = true;
|
||||
binaryOperatorTokenID = td.TokenId;
|
||||
operatorTokenNumber = i;
|
||||
}
|
||||
}
|
||||
|
||||
// check that we actually found one
|
||||
Debug.Assert(foundOperator);
|
||||
// if we found the operator, it means we also know its position number.
|
||||
Debug.Assert(operatorTokenNumber >= previousChild.Position.endTokenNumber);
|
||||
Debug.Assert(operatorTokenNumber < nextChild.Position.startTokenNumber);
|
||||
// and we know its type
|
||||
Debug.Assert(
|
||||
binaryOperatorTokenID == FormatterTokens.TOKEN_UNION ||
|
||||
binaryOperatorTokenID == FormatterTokens.TOKEN_INTERSECT ||
|
||||
binaryOperatorTokenID == FormatterTokens.TOKEN_EXCEPT);
|
||||
#endregion
|
||||
|
||||
// process the tokens before the operator:
|
||||
// [lastChild.Position.endTokenNumber, operatorTokenNumber)
|
||||
#region BeforeOperator
|
||||
|
||||
// If the first token is not a whitespace and it, we need to insert a newline in front
|
||||
TokenData endTokenData = GetTokenData(previousChild.Position.endTokenNumber);
|
||||
if (!IsTokenWhitespace(endTokenData))
|
||||
{
|
||||
AddIndentedNewLineReplacement(endTokenData.StartIndex);
|
||||
}
|
||||
|
||||
for (int i = previousChild.Position.endTokenNumber; i < operatorTokenNumber - 1; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
|
||||
if (CodeObject.Left is SqlQuerySpecification)
|
||||
{
|
||||
DecrementIndentLevel();
|
||||
}
|
||||
|
||||
if (operatorTokenNumber - 1 >= previousChild.Position.endTokenNumber)
|
||||
{
|
||||
SimpleProcessToken(operatorTokenNumber - 1, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
if (!IsTokenWhitespace(PreviousTokenData(operatorTokenNumber)))
|
||||
{
|
||||
TokenData td = GetTokenData(operatorTokenNumber);
|
||||
AddIndentedNewLineReplacement(td.StartIndex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion // BeforeOperator
|
||||
|
||||
// process the operator:
|
||||
// [operatorTokenNumber, firstTokenAfterOperator)
|
||||
#region Operator
|
||||
|
||||
// since the operator might contain more than one token, we will keep track of its end
|
||||
int firstTokenAfterOperator = operatorTokenNumber + 1;
|
||||
|
||||
// find where the operator ends
|
||||
if (binaryOperatorTokenID == FormatterTokens.TOKEN_UNION)
|
||||
{
|
||||
// the union operator may or may not be followed by the "ALL" modifier, so it might span over a number of tokens
|
||||
bool foundModifier = false;
|
||||
int modifierTokenNumber = nextChild.Position.startTokenNumber;
|
||||
|
||||
for (int i = operatorTokenNumber; !foundModifier && i < nextChild.Position.startTokenNumber; i++)
|
||||
{
|
||||
if (GetTokenData(i).TokenId == FormatterTokens.TOKEN_ALL)
|
||||
{
|
||||
foundModifier = true;
|
||||
modifierTokenNumber = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundModifier)
|
||||
{
|
||||
// leave everythong between "UNION" and "ALL" just as it was, but format the keywords
|
||||
firstTokenAfterOperator = modifierTokenNumber + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// only format the operator
|
||||
firstTokenAfterOperator = operatorTokenNumber + 1;
|
||||
}
|
||||
|
||||
ProcessTokenRange(operatorTokenNumber, firstTokenAfterOperator);
|
||||
|
||||
#endregion // Operator
|
||||
|
||||
// process tokens after the operator:
|
||||
// [firstTokenAfterOperator, nextChild.Position.startTokenNumber)
|
||||
#region AfterOperator
|
||||
|
||||
if (CodeObject.Right is SqlQuerySpecification)
|
||||
{
|
||||
IncrementIndentLevel();
|
||||
}
|
||||
|
||||
// if the first token is not a whitespace, we need to insert a newline in front
|
||||
if (!TokenManager.IsTokenWhitespace(TokenManager.TokenList[firstTokenAfterOperator].TokenId))
|
||||
{
|
||||
TokenData td = GetTokenData(firstTokenAfterOperator);
|
||||
AddIndentedNewLineReplacement(td.StartIndex);
|
||||
}
|
||||
|
||||
for (int i = firstTokenAfterOperator; i < nextChild.Position.startTokenNumber; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
|
||||
#endregion // AfterOperator
|
||||
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
if (CodeObject.Right is SqlQuerySpecification)
|
||||
{
|
||||
DecrementIndentLevel();
|
||||
}
|
||||
base.ProcessSuffixRegion(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlColumnDefinitionFormatterFactory : ASTNodeFormatterFactoryT<SqlColumnDefinition>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlColumnDefinition codeObject)
|
||||
{
|
||||
return new SqlColumnDefinitionFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlColumnDefinitionFormatter : ASTNodeFormatterT<SqlColumnDefinition>
|
||||
{
|
||||
private PaddedSpaceSeparatedListFormatter SpaceSeparatedListFormatter { get; set; }
|
||||
|
||||
internal SqlColumnDefinitionFormatter(FormatterVisitor visitor, SqlColumnDefinition codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
SpaceSeparatedListFormatter = new PaddedSpaceSeparatedListFormatter(visitor, codeObject, Visitor.Context.CurrentColumnSpacingFormatDefinitions, true);
|
||||
}
|
||||
|
||||
internal override void ProcessChild(SqlCodeObject child)
|
||||
{
|
||||
Validate.IsNotNull(nameof(child), child);
|
||||
SpaceSeparatedListFormatter.ProcessChild(child);
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
SpaceSeparatedListFormatter.ProcessPrefixRegion(startTokenNumber, firstChildStartTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
SpaceSeparatedListFormatter.ProcessSuffixRegion(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
SpaceSeparatedListFormatter.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// 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.Composition;
|
||||
using System.Diagnostics;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlCommonTableExpressionFormatterFactory : ASTNodeFormatterFactoryT<SqlCommonTableExpression>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlCommonTableExpression codeObject)
|
||||
{
|
||||
return new SqlCommonTableExpressionFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlCommonTableExpressionFormatter : SysCommentsFormatterBase<SqlCommonTableExpression>
|
||||
{
|
||||
|
||||
public SqlCommonTableExpressionFormatter(FormatterVisitor visitor, SqlCommonTableExpression codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool ShouldPlaceEachElementOnNewLine()
|
||||
{
|
||||
return FormatOptions.PlaceEachReferenceOnNewLineInQueryStatements;
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
IncrementIndentLevel();
|
||||
base.ProcessPrefixRegion(startTokenNumber, firstChildStartTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
DecrementIndentLevel();
|
||||
base.ProcessSuffixRegion(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
public override void Format()
|
||||
{
|
||||
int nextToken = ProcessExpressionName(CodeObject.Position.startTokenNumber);
|
||||
|
||||
nextToken = ProcessColumns(nextToken);
|
||||
|
||||
// TODO: should we indent the AS statement and then decrement indent at the end?
|
||||
nextToken = ProcessAsToken(nextToken, indentAfterAs: false);
|
||||
|
||||
nextToken = ProcessQueryExpression(nextToken);
|
||||
|
||||
}
|
||||
|
||||
private int ProcessQueryExpression(int nextToken)
|
||||
{
|
||||
NormalizeWhitespace normalizer = GetColumnWhitespaceNormalizer();
|
||||
nextToken = ProcessSectionInsideParentheses(nextToken,
|
||||
normalizer: FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum,
|
||||
isNewlineRequired: true,
|
||||
processSection: (n) => ProcessQuerySection(n, CodeObject.QueryExpression));
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
private int ProcessColumns(int nextToken)
|
||||
{
|
||||
if (CodeObject.ColumnList != null && CodeObject.ColumnList.Count > 0)
|
||||
{
|
||||
NormalizeWhitespace normalizer = GetColumnWhitespaceNormalizer();
|
||||
nextToken = ProcessSectionInsideParentheses(nextToken, normalizer,
|
||||
isNewlineRequired: FormatOptions.PlaceEachReferenceOnNewLineInQueryStatements,
|
||||
processSection: (n) => ProcessColumnList(n, CodeObject.ColumnList, normalizer));
|
||||
}
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
private NormalizeWhitespace GetColumnWhitespaceNormalizer()
|
||||
{
|
||||
if (FormatOptions.PlaceEachReferenceOnNewLineInQueryStatements)
|
||||
{
|
||||
return FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum;
|
||||
}
|
||||
return FormatterUtilities.NormalizeNewLinesOrCondenseToOneSpace;
|
||||
}
|
||||
|
||||
private int ProcessExpressionName(int nextToken)
|
||||
{
|
||||
SqlIdentifier name = CodeObject.Name;
|
||||
for (int i = nextToken; i < name.Position.startTokenNumber; i++)
|
||||
{
|
||||
ProcessTokenEnsuringOneNewLineMinimum(i);
|
||||
}
|
||||
|
||||
ProcessTokenRange(name.Position.startTokenNumber, name.Position.endTokenNumber);
|
||||
|
||||
nextToken = name.Position.endTokenNumber;
|
||||
|
||||
return nextToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using System.Composition;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlCompoundStatementFormatterFactory : ASTNodeFormatterFactoryT<SqlCompoundStatement>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlCompoundStatement codeObject)
|
||||
{
|
||||
return new SqlCompoundStatementFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
class SqlCompoundStatementFormatter : NewLineSeparatedListFormatter
|
||||
{
|
||||
internal SqlCompoundStatementFormatter(FormatterVisitor visitor, SqlCompoundStatement codeObject)
|
||||
: base(visitor, codeObject, true)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
|
||||
for (int i = startTokenNumber; i < firstChildStartTokenNumber; i++)
|
||||
{
|
||||
if (TokenManager.TokenList[i].TokenId == FormatterTokens.TOKEN_BEGIN_CS)
|
||||
{
|
||||
IncrementIndentLevel();
|
||||
}
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
DecrementIndentLevel();
|
||||
|
||||
for (int i = lastChildEndTokenNumber; i < endTokenNumber; i++)
|
||||
{
|
||||
if (TokenManager.TokenList[i].TokenId == FormatterTokens.TOKEN_END_CS
|
||||
&& !TokenManager.IsTokenWhitespace(TokenManager.TokenList[i-1].TokenId))
|
||||
{
|
||||
TokenData td = TokenManager.TokenList[i];
|
||||
AddIndentedNewLineReplacement(td.StartIndex);
|
||||
}
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 System.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlCreateProcedureStatementFormatterFactory : ASTNodeFormatterFactoryT<SqlCreateProcedureStatement>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlCreateProcedureStatement codeObject)
|
||||
{
|
||||
return new SqlCreateProcedureStatementFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
class SqlCreateProcedureStatementFormatter : NewLineSeparatedListFormatter
|
||||
{
|
||||
internal SqlCreateProcedureStatementFormatter(FormatterVisitor visitor, SqlCreateProcedureStatement codeObject)
|
||||
: base(visitor, codeObject, false)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
//
|
||||
// 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.Composition;
|
||||
using System.Diagnostics;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlCreateTableStatementFormatterFactory : ASTNodeFormatterFactoryT<SqlCreateTableStatement>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlCreateTableStatement codeObject)
|
||||
{
|
||||
return new SqlCreateTableStatementFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlCreateTableStatementFormatter : ASTNodeFormatterT<SqlCreateTableStatement>
|
||||
{
|
||||
internal SqlCreateTableStatementFormatter(FormatterVisitor visitor, SqlCreateTableStatement codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{ }
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
int nTokens = firstChildStartTokenNumber - startTokenNumber;
|
||||
Debug.Assert(nTokens >= 4, "unexpected token count for SqlCreateTableStatement prefix region");
|
||||
|
||||
int createTokenIndex = -1;
|
||||
int tableTokenIndex = -1;
|
||||
bool foundComment = false;
|
||||
for (int i = startTokenNumber; i < firstChildStartTokenNumber; i++)
|
||||
{
|
||||
TokenData td = TokenManager.TokenList[i];
|
||||
|
||||
if (td.TokenId == FormatterTokens.LEX_END_OF_LINE_COMMENT)
|
||||
{
|
||||
foundComment = true;
|
||||
} else if (td.TokenId == FormatterTokens.TOKEN_TABLE)
|
||||
{
|
||||
tableTokenIndex = i;
|
||||
} else if (td.TokenId == FormatterTokens.TOKEN_CREATE)
|
||||
{
|
||||
createTokenIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// logic below doesn't support single-line comments inside of a create table statement
|
||||
if (!foundComment && createTokenIndex < tableTokenIndex)
|
||||
{
|
||||
for (int i = startTokenNumber; i < firstChildStartTokenNumber; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeToOneSpace);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessTokenRange(startTokenNumber, firstChildStartTokenNumber);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
// Due to a current limitation of the parser, the suffix region stops right after the list of column definitions.
|
||||
// According to the TSQL grammar schema, the statement could continue (see: http://msdn.microsoft.com/en-us/library/ms174979.aspx).
|
||||
// We will preserve the text in the sufix region in its current formatting, with the exception that we ensure the closed parenthesis
|
||||
// which closes the list of column definitions is on a new line and that all the tokens preceding it (which should only be comments)
|
||||
// are also each on a separate line and indented
|
||||
|
||||
IncrementIndentLevel();
|
||||
|
||||
int closeParenToken = -1;
|
||||
|
||||
for (int i = lastChildEndTokenNumber; i < endTokenNumber && closeParenToken < 0; i++)
|
||||
{
|
||||
if (TokenManager.TokenList[i].TokenId == 41) closeParenToken = i;
|
||||
}
|
||||
|
||||
if (closeParenToken > 0)
|
||||
{
|
||||
for (int i = lastChildEndTokenNumber; i < closeParenToken - 1; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesInWhitespace);
|
||||
}
|
||||
|
||||
DecrementIndentLevel();
|
||||
|
||||
TokenData td2 = TokenManager.TokenList[closeParenToken - 1];
|
||||
|
||||
if (TokenManager.IsTokenWhitespace(td2.TokenId))
|
||||
{
|
||||
SimpleProcessToken(closeParenToken - 1, FormatterUtilities.NormalizeNewLinesInWhitespace);
|
||||
}
|
||||
else
|
||||
{
|
||||
TokenData td = TokenManager.TokenList[closeParenToken];
|
||||
AddIndentedNewLineReplacement(td.StartIndex);
|
||||
}
|
||||
|
||||
// Add the closed parenthesis and the additional unparsed elements of the statement
|
||||
// which should keep their old formatting
|
||||
ProcessTokenRange(closeParenToken, endTokenNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessTokenRange(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
if (previousChild is SqlObjectIdentifier && nextChild is SqlTableDefinition)
|
||||
{
|
||||
//
|
||||
// We want to make sure that the open-paren is on a new line and followed by a new-line & correctly indented.
|
||||
//
|
||||
|
||||
// find the open paren token
|
||||
int openParenToken = -1;
|
||||
for (int i = previousChild.Position.endTokenNumber; i < nextChild.Position.startTokenNumber; i++)
|
||||
{
|
||||
TokenData currentToken = TokenManager.TokenList[i];
|
||||
if (currentToken.TokenId == 40)
|
||||
{
|
||||
openParenToken = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// normalize whitespace between last token & open paren. Each whitespace token should be condensed down to a single space character
|
||||
for (int i = previousChild.Position.endTokenNumber; i < openParenToken - 1; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeToOneSpace);
|
||||
}
|
||||
|
||||
// If there is a whitespace before the open parenthisis, normalize it to a new line
|
||||
TokenData td = TokenManager.TokenList[openParenToken - 1];
|
||||
|
||||
if (TokenManager.IsTokenWhitespace(td.TokenId))
|
||||
{
|
||||
if (previousChild.Position.endTokenNumber < openParenToken)
|
||||
{
|
||||
SimpleProcessToken(openParenToken - 1, FormatterUtilities.NormalizeNewLinesInWhitespace);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (previousChild.Position.endTokenNumber < openParenToken)
|
||||
{
|
||||
SimpleProcessToken(openParenToken - 1, FormatterUtilities.NormalizeToOneSpace);
|
||||
}
|
||||
TokenData tok = TokenManager.TokenList[openParenToken];
|
||||
AddIndentedNewLineReplacement(tok.StartIndex);
|
||||
}
|
||||
|
||||
// append open-paren token
|
||||
ProcessTokenRange(openParenToken, openParenToken + 1);
|
||||
|
||||
// process tokens between open paren & first child start
|
||||
IncrementIndentLevel();
|
||||
for (int i = openParenToken + 1; i < nextChild.Position.startTokenNumber; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesInWhitespace);
|
||||
}
|
||||
|
||||
// ensure we have at least one new line
|
||||
if (openParenToken + 1 >= nextChild.Position.startTokenNumber || !TokenManager.IsTokenWhitespace(TokenManager.TokenList[nextChild.Position.startTokenNumber - 1].TokenId))
|
||||
{
|
||||
TokenData tok = TokenManager.TokenList[nextChild.Position.startTokenNumber];
|
||||
AddIndentedNewLineReplacement(tok.StartIndex);
|
||||
}
|
||||
DecrementIndentLevel();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessTokenRange(previousChild.Position.endTokenNumber, nextChild.Position.startTokenNumber);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.Composition;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlDataTypeFormatterFactory : ASTNodeFormatterFactoryT<SqlDataType>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlDataType codeObject)
|
||||
{
|
||||
return new SqlDataTypeFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlDataTypeFormatter : ASTNodeFormatterT<SqlDataType>
|
||||
{
|
||||
internal SqlDataTypeFormatter(FormatterVisitor visitor, SqlDataType codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
|
||||
public override void Format()
|
||||
{
|
||||
int startTokenIndex = CodeObject.Position.startTokenNumber;
|
||||
int endTokenIndex = CodeObject.Position.endTokenNumber;
|
||||
|
||||
if (startTokenIndex == endTokenIndex - 1 &&
|
||||
CodeObject.TokenManager.TokenList[startTokenIndex].TokenId == FormatterTokens.TOKEN_ID)
|
||||
{
|
||||
string sql = GetTokenRangeAsOriginalString(startTokenIndex, startTokenIndex + 1);
|
||||
if (FormatOptions.UppercaseDataTypes)
|
||||
{
|
||||
TokenData tok = TokenManager.TokenList[startTokenIndex];
|
||||
AddReplacement(tok.StartIndex, sql, sql.ToUpperInvariant());
|
||||
sql = sql.ToUpperInvariant();
|
||||
}
|
||||
else if (FormatOptions.LowercaseDataTypes)
|
||||
{
|
||||
TokenData tok = TokenManager.TokenList[startTokenIndex];
|
||||
AddReplacement(tok.StartIndex, sql, sql.ToLowerInvariant());
|
||||
sql = sql.ToLowerInvariant();
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Format();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlFromClauseFormatterFactory : ASTNodeFormatterFactoryT<SqlFromClause>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlFromClause codeObject)
|
||||
{
|
||||
return new SqlFromClauseFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlFromClauseFormatter : ASTNodeFormatterT<SqlFromClause>
|
||||
{
|
||||
private CommaSeparatedListFormatter CommaSeparatedListFormatter { get; set; }
|
||||
|
||||
internal SqlFromClauseFormatter(FormatterVisitor visitor, SqlFromClause codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
CommaSeparatedListFormatter = new CommaSeparatedListFormatter(visitor, codeObject, FormatOptions.PlaceEachReferenceOnNewLineInQueryStatements);
|
||||
}
|
||||
|
||||
internal override void ProcessChild(SqlCodeObject child)
|
||||
{
|
||||
Validate.IsNotNull(nameof(child), child);
|
||||
CommaSeparatedListFormatter.ProcessChild(child);
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
CommaSeparatedListFormatter.ProcessPrefixRegion(startTokenNumber, firstChildStartTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
CommaSeparatedListFormatter.ProcessSuffixRegion(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
CommaSeparatedListFormatter.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.Generic;
|
||||
using System.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlInsertSpecificationFormatterFactory : ASTNodeFormatterFactoryT<SqlInsertSpecification>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlInsertSpecification codeObject)
|
||||
{
|
||||
return new SqlInsertSpecificationFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
class SqlInsertSpecificationFormatter : ASTNodeFormatterT<SqlInsertSpecification>
|
||||
{
|
||||
internal SqlInsertSpecificationFormatter(FormatterVisitor visitor, SqlInsertSpecification codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Format()
|
||||
{
|
||||
|
||||
IEnumerator<SqlCodeObject> firstChildEnum = CodeObject.Children.GetEnumerator();
|
||||
if (firstChildEnum.MoveNext())
|
||||
{
|
||||
//
|
||||
// format the text from the start of the object to the start of it's first child
|
||||
//
|
||||
ProcessPrefixRegion(CodeObject.Position.startTokenNumber, firstChildEnum.Current.Position.startTokenNumber);
|
||||
int nextToken = firstChildEnum.Current.Position.startTokenNumber;
|
||||
|
||||
// handle top specification
|
||||
nextToken = ProcessTopSpecification(nextToken);
|
||||
|
||||
// handle target
|
||||
nextToken = ProcessTarget(nextToken);
|
||||
|
||||
// handle target columns
|
||||
nextToken = ProcessColumns(nextToken);
|
||||
|
||||
// handle output clause
|
||||
nextToken = ProcessOutputClause(nextToken);
|
||||
|
||||
// handle values / derived table / execute statement / dml_table_source
|
||||
nextToken = ProcessValues(nextToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatFailedException("Insert statement has no children.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal int ProcessTopSpecification(int nextToken)
|
||||
{
|
||||
if (CodeObject.TopSpecification != null)
|
||||
{
|
||||
ProcessAndNormalizeWhitespaceRange(nextToken, CodeObject.TopSpecification.Position.startTokenNumber,
|
||||
FormatterUtilities.NormalizeNewLinesOrCondenseToOneSpace);
|
||||
|
||||
ProcessChild(CodeObject.TopSpecification);
|
||||
|
||||
nextToken = CodeObject.TopSpecification.Position.endTokenNumber;
|
||||
}
|
||||
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
|
||||
private int ProcessTarget(int nextToken)
|
||||
{
|
||||
Debug.Assert(CodeObject.Target != null, "No target in insert statement.");
|
||||
|
||||
// find out if there is an "INTO" token
|
||||
int intoTokenIndexOrTargetStartTokenIndex = CodeObject.Target.Position.startTokenNumber;
|
||||
for (int i = nextToken; i < CodeObject.Target.Position.startTokenNumber; i++)
|
||||
{
|
||||
if (TokenManager.TokenList[i].TokenId == FormatterTokens.TOKEN_INTO)
|
||||
{
|
||||
intoTokenIndexOrTargetStartTokenIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Process up to the INTO or Target index. If INTO isn't there, expect all whitespace tokens
|
||||
ProcessAndNormalizeWhitespaceRange(nextToken, intoTokenIndexOrTargetStartTokenIndex,
|
||||
FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
|
||||
IncrementIndentLevel();
|
||||
|
||||
// Process the INTO token and all whitespace up to the target start
|
||||
ProcessAndNormalizeTokenRange(intoTokenIndexOrTargetStartTokenIndex, CodeObject.Target.Position.startTokenNumber,
|
||||
FormatterUtilities.NormalizeNewLinesOrCondenseToOneSpace, areAllTokensWhitespace: false);
|
||||
|
||||
ProcessChild(CodeObject.Target);
|
||||
|
||||
nextToken = CodeObject.Target.Position.endTokenNumber;
|
||||
DecrementIndentLevel();
|
||||
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
private int ProcessColumns(int nextToken)
|
||||
{
|
||||
if (CodeObject.TargetColumns != null)
|
||||
{
|
||||
if (CodeObject.TargetColumns.Count > 0)
|
||||
{
|
||||
IncrementIndentLevel();
|
||||
|
||||
// if the next token is not a whitespace, a newline is enforced.
|
||||
TokenData nextTokenData = GetTokenData(nextToken);
|
||||
if (!IsTokenWhitespace(nextTokenData))
|
||||
{
|
||||
AddIndentedNewLineReplacement(nextTokenData.StartIndex);
|
||||
}
|
||||
|
||||
NormalizeWhitespace f = FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum;
|
||||
|
||||
// process tokens until we reach the closed parenthesis (with id 41)
|
||||
for (int id = TokenManager.TokenList[nextToken].TokenId; id != 41; id = TokenManager.TokenList[++nextToken].TokenId)
|
||||
{
|
||||
SimpleProcessToken(nextToken, f);
|
||||
if (id == 40) // open parenthesis (id == 40) changes the formatting
|
||||
{
|
||||
f = FormatterUtilities.NormalizeNewLinesOrCondenseToOneSpace;
|
||||
}
|
||||
}
|
||||
|
||||
// process the cosed paren
|
||||
SimpleProcessToken(nextToken, f);
|
||||
|
||||
nextToken++;
|
||||
|
||||
DecrementIndentLevel();
|
||||
}
|
||||
}
|
||||
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
|
||||
private int ProcessValues(int nextToken)
|
||||
{
|
||||
if (CodeObject.Source != null && HasToken(nextToken))
|
||||
{
|
||||
TokenData nextTokenData = GetTokenData(nextToken);
|
||||
// if the next token is not a whitespace, a newline is enforced.
|
||||
if (!IsTokenWhitespace(nextTokenData))
|
||||
{
|
||||
AddIndentedNewLineReplacement(nextTokenData.StartIndex);
|
||||
}
|
||||
|
||||
ProcessAndNormalizeWhitespaceRange(nextToken, CodeObject.Source.Position.startTokenNumber,
|
||||
FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
|
||||
ProcessChild(CodeObject.Source);
|
||||
}
|
||||
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
private int ProcessOutputClause(int nextToken)
|
||||
{
|
||||
if (CodeObject.OutputIntoClause != null)
|
||||
{
|
||||
if (nextToken == CodeObject.OutputIntoClause.Position.startTokenNumber)
|
||||
{
|
||||
AddIndentedNewLineReplacement(GetTokenData(nextToken).StartIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (nextToken < CodeObject.OutputIntoClause.Position.startTokenNumber)
|
||||
{
|
||||
DebugAssertTokenIsWhitespaceOrComment(GetTokenData(nextToken), nextToken);
|
||||
SimpleProcessToken(nextToken, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
nextToken++;
|
||||
}
|
||||
|
||||
TokenData previousTokenData = PreviousTokenData(nextToken);
|
||||
if (!IsTokenWhitespace(previousTokenData))
|
||||
{
|
||||
AddIndentedNewLineReplacement(previousTokenData.StartIndex);
|
||||
}
|
||||
}
|
||||
ProcessChild(CodeObject.OutputIntoClause);
|
||||
nextToken = CodeObject.OutputIntoClause.Position.endTokenNumber;
|
||||
}
|
||||
|
||||
return nextToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Composition;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlInsertStatementFormatterFactory : ASTNodeFormatterFactoryT<SqlInsertStatement>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlInsertStatement codeObject)
|
||||
{
|
||||
return new SqlInsertStatementFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlInsertStatementFormatter : NewLineSeparatedListFormatter
|
||||
{
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
public SqlInsertStatementFormatter(FormatterVisitor visitor, SqlInsertStatement codeObject)
|
||||
: base(visitor, codeObject, false)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// 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.Composition;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlProcedureDefinitionFormatterFactory : ASTNodeFormatterFactoryT<SqlProcedureDefinition>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlProcedureDefinition codeObject)
|
||||
{
|
||||
return new SqlProcedureDefinitionFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
class SqlProcedureDefinitionFormatter : CommaSeparatedListFormatter
|
||||
{
|
||||
NewLineSeparatedListFormatter NewLineSeparatedListFormatter { get; set; }
|
||||
bool foundTokenWith;
|
||||
|
||||
internal SqlProcedureDefinitionFormatter(FormatterVisitor visitor, SqlProcedureDefinition codeObject)
|
||||
: base(visitor, codeObject, true)
|
||||
{
|
||||
NewLineSeparatedListFormatter = new NewLineSeparatedListFormatter(visitor, codeObject, false);
|
||||
foundTokenWith = false;
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
if (nextChild is SqlModuleOption)
|
||||
{
|
||||
if (!foundTokenWith)
|
||||
{
|
||||
DecrementIndentLevel();
|
||||
}
|
||||
for (int i = previousChild.Position.endTokenNumber; i < nextChild.Position.startTokenNumber; i++)
|
||||
{
|
||||
if (!foundTokenWith && TokenManager.TokenList[i].TokenId == FormatterTokens.TOKEN_WITH)
|
||||
{
|
||||
IncrementIndentLevel();
|
||||
foundTokenWith = true;
|
||||
}
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
}
|
||||
else if (previousChild is SqlObjectIdentifier)
|
||||
{
|
||||
NewLineSeparatedListFormatter.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
DecrementIndentLevel();
|
||||
NormalizeWhitespace f = FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum;
|
||||
for (int i = lastChildEndTokenNumber; i < endTokenNumber; i++)
|
||||
{
|
||||
if (TokenManager.TokenList[i].TokenId == FormatterTokens.TOKEN_AS
|
||||
&& !TokenManager.IsTokenWhitespace(TokenManager.TokenList[i-1].TokenId))
|
||||
{
|
||||
TokenData td = TokenManager.TokenList[i];
|
||||
AddIndentedNewLineReplacement(td.StartIndex);
|
||||
}
|
||||
if (TokenManager.TokenList[i].TokenId == FormatterTokens.TOKEN_FOR)
|
||||
{
|
||||
f = FormatterUtilities.NormalizeNewLinesOrCondenseToOneSpace;
|
||||
}
|
||||
else if (TokenManager.TokenList[i].TokenId == FormatterTokens.TOKEN_REPLICATION)
|
||||
{
|
||||
f = FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum;
|
||||
}
|
||||
SimpleProcessToken(i, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlQualifiedJoinTableExpressionFormatterFactory : ASTNodeFormatterFactoryT<SqlQualifiedJoinTableExpression>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlQualifiedJoinTableExpression codeObject)
|
||||
{
|
||||
return new SqlQualifiedJoinTableExpressionFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlQualifiedJoinTableExpressionFormatter : ASTNodeFormatterT<SqlQualifiedJoinTableExpression>
|
||||
{
|
||||
SpaceSeparatedListFormatter SpaceSeparatedListFormatter { get; set; }
|
||||
|
||||
internal SqlQualifiedJoinTableExpressionFormatter(FormatterVisitor visitor, SqlQualifiedJoinTableExpression codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
SpaceSeparatedListFormatter = new SpaceSeparatedListFormatter(visitor, codeObject, false);
|
||||
}
|
||||
|
||||
internal override void ProcessChild(SqlCodeObject child)
|
||||
{
|
||||
Validate.IsNotNull(nameof(child), child);
|
||||
SpaceSeparatedListFormatter.ProcessChild(child);
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
SpaceSeparatedListFormatter.ProcessPrefixRegion(startTokenNumber, firstChildStartTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
SpaceSeparatedListFormatter.ProcessSuffixRegion(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
SpaceSeparatedListFormatter.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlQuerySpecificationFormatterFactory : ASTNodeFormatterFactoryT<SqlQuerySpecification>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlQuerySpecification codeObject)
|
||||
{
|
||||
return new SqlQuerySpecificationFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlQuerySpecificationFormatter : ASTNodeFormatterT<SqlQuerySpecification>
|
||||
{
|
||||
WhiteSpaceSeparatedListFormatter WhiteSpaceSeparatedListFormatter { get; set; }
|
||||
|
||||
internal SqlQuerySpecificationFormatter(FormatterVisitor visitor, SqlQuerySpecification codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
WhiteSpaceSeparatedListFormatter = new NewLineSeparatedListFormatter(visitor, codeObject, false);
|
||||
}
|
||||
|
||||
internal override void ProcessChild(SqlCodeObject child)
|
||||
{
|
||||
Validate.IsNotNull(nameof(child), child);
|
||||
WhiteSpaceSeparatedListFormatter.ProcessChild(child);
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
WhiteSpaceSeparatedListFormatter.ProcessPrefixRegion(startTokenNumber, firstChildStartTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
WhiteSpaceSeparatedListFormatter.ProcessSuffixRegion(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
WhiteSpaceSeparatedListFormatter.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 System.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlQueryWithClauseFormatterFactory : ASTNodeFormatterFactoryT<SqlQueryWithClause>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlQueryWithClause codeObject)
|
||||
{
|
||||
return new SqlQueryWithClauseFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
class SqlQueryWithClauseFormatter : CommaSeparatedListFormatter
|
||||
{
|
||||
public SqlQueryWithClauseFormatter(FormatterVisitor visitor, SqlQueryWithClause codeObject)
|
||||
: base(visitor, codeObject, true)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Composition;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlRowConstructorExpressionFormatterFactory : ASTNodeFormatterFactoryT<SqlRowConstructorExpression>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlRowConstructorExpression codeObject)
|
||||
{
|
||||
return new SqlRowConstructorExpressionFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlRowConstructorExpressionFormatter : CommaSeparatedListFormatter
|
||||
{
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
public SqlRowConstructorExpressionFormatter(FormatterVisitor visitor, SqlRowConstructorExpression codeObject)
|
||||
: base(visitor, codeObject, false)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlSelectClauseFormatterFactory : ASTNodeFormatterFactoryT<SqlSelectClause>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlSelectClause codeObject)
|
||||
{
|
||||
return new SqlSelectClauseFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlSelectClauseFormatter : CommaSeparatedListFormatter
|
||||
{
|
||||
private NewLineSeparatedListFormatter NewLineSeparatedListFormatter { get; set; }
|
||||
|
||||
internal SqlSelectClauseFormatter(FormatterVisitor visitor, SqlSelectClause codeObject)
|
||||
: base(visitor, codeObject, visitor.Context.FormatOptions.PlaceEachReferenceOnNewLineInQueryStatements)
|
||||
{
|
||||
NewLineSeparatedListFormatter = new NewLineSeparatedListFormatter(visitor, codeObject, true);
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
if (previousChild is SqlTopSpecification)
|
||||
{
|
||||
NewLineSeparatedListFormatter.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// 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.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlSelectSpecificationFormatterFactory : ASTNodeFormatterFactoryT<SqlSelectSpecification>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlSelectSpecification codeObject)
|
||||
{
|
||||
return new SqlSelectSpecificationFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlSelectSpecificationFormatter : NewLineSeparatedListFormatter
|
||||
{
|
||||
|
||||
internal SqlSelectSpecificationFormatter(FormatterVisitor visitor, SqlSelectSpecification codeObject)
|
||||
: base(visitor, codeObject, false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal override void ProcessChild(SqlCodeObject child)
|
||||
{
|
||||
Validate.IsNotNull(nameof(child), child);
|
||||
base.ProcessChild(child);
|
||||
if (child is SqlForBrowseClause)
|
||||
{
|
||||
DecrementIndentLevel();
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
for (int i = lastChildEndTokenNumber; i < endTokenNumber; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
/* Due to the current behavior of the T-SQL Parser, the FOR clause needs to be treated separately */
|
||||
if (nextChild is SqlForBrowseClause || nextChild is SqlForXmlClause)
|
||||
{
|
||||
#region Find the "FOR" token
|
||||
int forTokenIndex = previousChild.Position.endTokenNumber;
|
||||
TokenData td = TokenManager.TokenList[forTokenIndex];
|
||||
while (td.TokenId != FormatterTokens.TOKEN_FOR && forTokenIndex < CodeObject.Position.endTokenNumber)
|
||||
{
|
||||
Debug.Assert(
|
||||
TokenManager.IsTokenComment(td.TokenId)
|
||||
|| TokenManager.IsTokenWhitespace(td.TokenId)
|
||||
, string.Format(CultureInfo.CurrentCulture, "Unexpected token \"{0}\" before the FOR token.", Visitor.Context.GetTokenRangeAsOriginalString(forTokenIndex, forTokenIndex + 1))
|
||||
);
|
||||
++forTokenIndex;
|
||||
td = TokenManager.TokenList[forTokenIndex];
|
||||
}
|
||||
Debug.Assert(forTokenIndex < CodeObject.Position.endTokenNumber, "No FOR token.");
|
||||
#endregion // Find the "FOR" token
|
||||
|
||||
|
||||
#region Process the tokens before the "FOR" token
|
||||
for (int i = previousChild.Position.endTokenNumber; i < forTokenIndex; i++)
|
||||
{
|
||||
Debug.Assert(
|
||||
TokenManager.IsTokenComment(TokenManager.TokenList[i].TokenId)
|
||||
|| TokenManager.IsTokenWhitespace(TokenManager.TokenList[i].TokenId)
|
||||
, string.Format(CultureInfo.CurrentCulture, "Unexpected token \"{0}\" before the FOR token.", Visitor.Context.GetTokenRangeAsOriginalString(i, i + 1))
|
||||
);
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
#endregion // Process the tokens before the "FOR" token
|
||||
|
||||
#region Process the "FOR" token
|
||||
if (previousChild.Position.endTokenNumber >= forTokenIndex
|
||||
|| !TokenManager.IsTokenWhitespace(TokenManager.TokenList[forTokenIndex - 1].TokenId))
|
||||
{
|
||||
td = TokenManager.TokenList[forTokenIndex];
|
||||
AddIndentedNewLineReplacement(td.StartIndex);
|
||||
}
|
||||
Visitor.Context.ProcessTokenRange(forTokenIndex, forTokenIndex + 1);
|
||||
IncrementIndentLevel();
|
||||
|
||||
int nextToken = forTokenIndex + 1;
|
||||
Debug.Assert(nextToken < CodeObject.Position.endTokenNumber, "View Definition ends unexpectedly after the FOR token.");
|
||||
// Ensure a whitespace after the "FOR" token
|
||||
if (!TokenManager.IsTokenWhitespace(TokenManager.TokenList[nextToken].TokenId))
|
||||
{
|
||||
td = TokenManager.TokenList[forTokenIndex];
|
||||
AddIndentedNewLineReplacement(td.StartIndex);
|
||||
}
|
||||
#endregion // Process the "FOR" token
|
||||
|
||||
#region Process tokens after the FOR token
|
||||
for (int i = nextToken; i < nextChild.Position.startTokenNumber; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatterUtilities.NormalizeNewLinesOrCondenseToOneSpace);
|
||||
}
|
||||
#endregion // Process tokens after the FOR token
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 System.Composition;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlSelectStatementFormatterFactory : ASTNodeFormatterFactoryT<SqlSelectStatement>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlSelectStatement codeObject)
|
||||
{
|
||||
return new SqlSelectStatementFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
class SqlSelectStatementFormatter : NewLineSeparatedListFormatter
|
||||
{
|
||||
|
||||
internal SqlSelectStatementFormatter(FormatterVisitor visitor, SqlSelectStatement codeObject)
|
||||
: base(visitor, codeObject, false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Composition;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlTableConstructorExpressionFormatterFactory : ASTNodeFormatterFactoryT<SqlTableConstructorExpression>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlTableConstructorExpression codeObject)
|
||||
{
|
||||
return new SqlTableConstructorExpressionFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlTableConstructorExpressionFormatter : CommaSeparatedListFormatter
|
||||
{
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
public SqlTableConstructorExpressionFormatter(FormatterVisitor visitor, SqlTableConstructorExpression codeObject)
|
||||
: base(visitor, codeObject, true)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// 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.Composition;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlTableDefinitionFormatterFactory : ASTNodeFormatterFactoryT<SqlTableDefinition>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlTableDefinition codeObject)
|
||||
{
|
||||
return new SqlTableDefinitionFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ASTNodeFormatter))]
|
||||
internal class SqlTableDefinitionFormatter : ASTNodeFormatterT<SqlTableDefinition>
|
||||
{
|
||||
private CommaSeparatedListFormatter CommaSeparatedListFormatter { get; set; }
|
||||
|
||||
public SqlTableDefinitionFormatter(FormatterVisitor visitor, SqlTableDefinition codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
CommaSeparatedListFormatter = new CommaSeparatedListFormatter(visitor, codeObject, true);
|
||||
|
||||
// figure out the size of paddings required to align column definitions in a "columnar" form
|
||||
if (FormatOptions.AlignColumnDefinitionsInColumns)
|
||||
{
|
||||
int range1MaxLength = 0;
|
||||
int range2MaxLength = 0;
|
||||
|
||||
foreach (SqlCodeObject child in CodeObject.Children)
|
||||
{
|
||||
if (child is SqlColumnDefinition && !(child is SqlComputedColumnDefinition))
|
||||
{
|
||||
SqlIdentifier identifierChild = child.Children.ElementAtOrDefault(0) as SqlIdentifier;
|
||||
|
||||
if (identifierChild == null)
|
||||
{
|
||||
throw new FormatFailedException("unexpected token at index start Token Index");
|
||||
}
|
||||
|
||||
string s1 = child.TokenManager.GetText(identifierChild.Position.startTokenNumber, identifierChild.Position.endTokenNumber);
|
||||
range1MaxLength = Math.Max(range1MaxLength, s1.Length);
|
||||
|
||||
SqlDataTypeSpecification dataTypeChildchild = child.Children.ElementAtOrDefault(1) as SqlDataTypeSpecification;
|
||||
|
||||
// token "timestamp" should be ignorred
|
||||
if (dataTypeChildchild != null)
|
||||
{
|
||||
string s2 = child.TokenManager.GetText(dataTypeChildchild.Position.startTokenNumber, dataTypeChildchild.Position.endTokenNumber);
|
||||
range2MaxLength = Math.Max(range2MaxLength, s2.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PaddedSpaceSeparatedListFormatter.ColumnSpacingFormatDefinition d1 = new PaddedSpaceSeparatedListFormatter.ColumnSpacingFormatDefinition(typeof(SqlIdentifier), typeof(SqlDataTypeSpecification), range1MaxLength + 1);
|
||||
PaddedSpaceSeparatedListFormatter.ColumnSpacingFormatDefinition d2 = new PaddedSpaceSeparatedListFormatter.ColumnSpacingFormatDefinition(typeof(SqlDataTypeSpecification), null, range2MaxLength + 1);
|
||||
List<PaddedSpaceSeparatedListFormatter.ColumnSpacingFormatDefinition> columnSpacingFormatDefinitions = new List<PaddedSpaceSeparatedListFormatter.ColumnSpacingFormatDefinition>(2);
|
||||
columnSpacingFormatDefinitions.Add(d1);
|
||||
columnSpacingFormatDefinitions.Add(d2);
|
||||
Visitor.Context.CurrentColumnSpacingFormatDefinitions = columnSpacingFormatDefinitions;
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ProcessChild(SqlCodeObject child)
|
||||
{
|
||||
Validate.IsNotNull(nameof(child), child);
|
||||
CommaSeparatedListFormatter.ProcessChild(child);
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
CommaSeparatedListFormatter.ProcessPrefixRegion(startTokenNumber, firstChildStartTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
Visitor.Context.CurrentColumnSpacingFormatDefinitions = null;
|
||||
CommaSeparatedListFormatter.ProcessSuffixRegion(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
CommaSeparatedListFormatter.ProcessInterChildRegion(previousChild, nextChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// 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.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(ASTNodeFormatterFactory))]
|
||||
internal class SqlViewDefinitionFormatterFactory : ASTNodeFormatterFactoryT<SqlViewDefinition>
|
||||
{
|
||||
protected override ASTNodeFormatter DoCreate(FormatterVisitor visitor, SqlViewDefinition codeObject)
|
||||
{
|
||||
return new SqlViewDefinitionFormatter(visitor, codeObject);
|
||||
}
|
||||
}
|
||||
|
||||
class SqlViewDefinitionFormatter : SysCommentsFormatterBase<SqlViewDefinition>
|
||||
{
|
||||
|
||||
internal SqlViewDefinitionFormatter(FormatterVisitor visitor, SqlViewDefinition sqlCodeObject)
|
||||
: base(visitor, sqlCodeObject)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool ShouldPlaceEachElementOnNewLine()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Format()
|
||||
{
|
||||
LexLocation loc = CodeObject.Position;
|
||||
|
||||
SqlCodeObject firstChild = CodeObject.Children.FirstOrDefault();
|
||||
if (firstChild != null)
|
||||
{
|
||||
//
|
||||
// format the text from the start of the object to the start of its first child
|
||||
//
|
||||
LexLocation firstChildStart = firstChild.Position;
|
||||
ProcessPrefixRegion(loc.startTokenNumber, firstChildStart.startTokenNumber);
|
||||
|
||||
ProcessChild(firstChild);
|
||||
|
||||
// keep track of the next token to process
|
||||
int nextToken = firstChildStart.endTokenNumber;
|
||||
|
||||
// process the columns if available
|
||||
nextToken = ProcessColumns(nextToken);
|
||||
|
||||
// process options if available
|
||||
nextToken = ProcessOptions(nextToken);
|
||||
|
||||
// process the region containing the AS token
|
||||
nextToken = ProcessAsToken(nextToken, indentAfterAs: true);
|
||||
|
||||
// process the query with clause if present
|
||||
nextToken = ProcessQueryWithClause(nextToken);
|
||||
|
||||
// process the query expression
|
||||
nextToken = ProcessQueryExpression(nextToken);
|
||||
|
||||
DecrementIndentLevel();
|
||||
|
||||
// format text from end of last child to end of object.
|
||||
SqlCodeObject lastChild = CodeObject.Children.LastOrDefault();
|
||||
Debug.Assert(lastChild != null, "last child is null. Need to write code to deal with this case");
|
||||
ProcessSuffixRegion(lastChild.Position.endTokenNumber, loc.endTokenNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no children
|
||||
Visitor.Context.ProcessTokenRange(loc.startTokenNumber, loc.endTokenNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private int ProcessColumns(int nextToken)
|
||||
{
|
||||
if (CodeObject.ColumnList != null && CodeObject.ColumnList.Count > 0)
|
||||
{
|
||||
nextToken = ProcessSectionInsideParentheses(nextToken, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum,
|
||||
isNewlineRequired: true,
|
||||
processSection: (n) => ProcessColumnList(n, CodeObject.ColumnList, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum));
|
||||
}
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
private int ProcessOptions(int nextToken)
|
||||
{
|
||||
if (CodeObject.Options != null && CodeObject.Options.Count > 0)
|
||||
{
|
||||
int withTokenIndex = FindTokenWithId(nextToken, FormatterTokens.TOKEN_WITH);
|
||||
|
||||
// Preprocess
|
||||
ProcessTokenRangeEnsuringOneNewLineMinumum(nextToken, withTokenIndex);
|
||||
|
||||
nextToken = ProcessWithStatementStart(nextToken, withTokenIndex);
|
||||
|
||||
nextToken = ProcessOptionsSection(nextToken);
|
||||
|
||||
DecrementIndentLevel();
|
||||
}
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
private int ProcessOptionsSection(int nextToken)
|
||||
{
|
||||
// find where the options start
|
||||
IEnumerator<SqlModuleOption> optionEnum = CodeObject.Options.GetEnumerator();
|
||||
if (optionEnum.MoveNext())
|
||||
{
|
||||
ProcessAndNormalizeWhitespaceRange(nextToken, optionEnum.Current.Position.startTokenNumber,
|
||||
FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
|
||||
// Process options
|
||||
ProcessChild(optionEnum.Current);
|
||||
SqlModuleOption previousOption = optionEnum.Current;
|
||||
while (optionEnum.MoveNext())
|
||||
{
|
||||
CommaSeparatedList.ProcessInterChildRegion(previousOption, optionEnum.Current);
|
||||
ProcessChild(optionEnum.Current);
|
||||
previousOption = optionEnum.Current;
|
||||
}
|
||||
nextToken = previousOption.Position.endTokenNumber;
|
||||
}
|
||||
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
private int ProcessQueryWithClause(int nextToken)
|
||||
{
|
||||
return ProcessQuerySection(nextToken, CodeObject.QueryWithClause);
|
||||
}
|
||||
|
||||
private int ProcessQueryExpression(int nextToken)
|
||||
{
|
||||
return ProcessQuerySection(nextToken, CodeObject.QueryExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// 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.Diagnostics;
|
||||
using Babel.ParserGenerator;
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
/// <summary>
|
||||
/// Common base class for objects dealing with sys comments. These follow
|
||||
/// similar patterns so identical methods are held here
|
||||
/// </summary>
|
||||
internal abstract class SysCommentsFormatterBase<T> : ASTNodeFormatterT<T>
|
||||
where T : SqlCodeObject
|
||||
{
|
||||
|
||||
internal CommaSeparatedListFormatter CommaSeparatedList { get; set; }
|
||||
|
||||
public SysCommentsFormatterBase(FormatterVisitor visitor, T codeObject)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
CommaSeparatedList = new CommaSeparatedListFormatter(Visitor, CodeObject, ShouldPlaceEachElementOnNewLine());
|
||||
}
|
||||
|
||||
protected abstract bool ShouldPlaceEachElementOnNewLine();
|
||||
|
||||
protected void ProcessTokenEnsuringOneNewLineMinimum(int tokenIndex)
|
||||
{
|
||||
ProcessTokenAndNormalize(tokenIndex, FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// processes any section in a query, since the basic behavior is constant
|
||||
/// </summary>
|
||||
protected int ProcessQuerySection(int nextToken, SqlCodeObject queryObject)
|
||||
{
|
||||
if (queryObject != null)
|
||||
{
|
||||
ProcessAndNormalizeWhitespaceRange(nextToken, queryObject.Position.startTokenNumber,
|
||||
FormatterUtilities.NormalizeNewLinesEnsureOneNewLineMinimum);
|
||||
ProcessChild(queryObject);
|
||||
nextToken = queryObject.Position.endTokenNumber;
|
||||
}
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
protected int ProcessSectionInsideParentheses(int nextToken, NormalizeWhitespace normalizer, bool isNewlineRequired,
|
||||
Func<int, int> processSection)
|
||||
{
|
||||
int openParenIndex = FindOpenParenthesis(nextToken);
|
||||
ProcessAndNormalizeWhitespaceRange(nextToken, openParenIndex, normalizer);
|
||||
nextToken = ProcessOpenParenthesis(nextToken, openParenIndex, isNewlineRequired);
|
||||
|
||||
nextToken = processSection(nextToken);
|
||||
|
||||
int closedParenIndex = FindClosedParenthesis(nextToken);
|
||||
|
||||
ProcessRegionBeforeClosedParenthesis(nextToken, closedParenIndex, normalizer, isNewlineRequired);
|
||||
|
||||
// Process closed parenthesis
|
||||
ProcessTokenRange(closedParenIndex, closedParenIndex + 1);
|
||||
nextToken = closedParenIndex + 1;
|
||||
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
protected int FindOpenParenthesis(int openParenIndex)
|
||||
{
|
||||
return FindTokenWithId(openParenIndex, 40);
|
||||
}
|
||||
|
||||
protected int FindClosedParenthesis(int nextToken)
|
||||
{
|
||||
return FindTokenWithId(nextToken, 41);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// if there was no whitespace before the parenthesis to be converted into a newline,
|
||||
/// and the references need to be on a newline, then append a newline
|
||||
/// </summary>
|
||||
protected int ProcessOpenParenthesis(int nextToken, int openParenIndex, bool isNewlineRequired)
|
||||
{
|
||||
return ProcessCompoundStatementStart(ref nextToken, openParenIndex, isNewlineRequired);
|
||||
}
|
||||
|
||||
protected int ProcessWithStatementStart(int nextToken, int withTokenIndex)
|
||||
{
|
||||
return ProcessCompoundStatementStart(ref nextToken, withTokenIndex, true);
|
||||
}
|
||||
|
||||
protected int ProcessCompoundStatementStart(ref int nextToken, int compoundStartIndex, bool isNewlineRequired)
|
||||
{
|
||||
// if a newline is required and there was no whitespace before the start to be
|
||||
// converted into a newline, then append a newline
|
||||
if (isNewlineRequired
|
||||
&& (nextToken >= compoundStartIndex
|
||||
|| !IsTokenWhitespace(PreviousTokenData(compoundStartIndex))))
|
||||
{
|
||||
// Note: nextToken index value does not match the Startindex of the TokenData. When adding
|
||||
// indentation, always get the TokenData and its StartIndex value
|
||||
TokenData td = GetTokenData(compoundStartIndex);
|
||||
AddIndentedNewLineReplacement(td.StartIndex);
|
||||
}
|
||||
ProcessTokenRange(compoundStartIndex, compoundStartIndex + 1);
|
||||
IncrementIndentLevel();
|
||||
|
||||
// Move our pointer past the start of the compount statement
|
||||
nextToken = compoundStartIndex + 1;
|
||||
TokenData nextTokenData = GetTokenData(nextToken);
|
||||
|
||||
// Ensure a newline after the open parenthesis
|
||||
if (isNewlineRequired
|
||||
&& !IsTokenWhitespace(nextTokenData))
|
||||
{
|
||||
AddIndentedNewLineReplacement(nextTokenData.StartIndex);
|
||||
}
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
protected void ProcessRegionBeforeClosedParenthesis(int startIndex, int closedParenIndex, NormalizeWhitespace normalizer, bool isNewlineRequired)
|
||||
{
|
||||
for (int i = startIndex; i < closedParenIndex - 1; i++)
|
||||
{
|
||||
ProcessTokenAndNormalize(i, normalizer);
|
||||
}
|
||||
DecrementIndentLevel();
|
||||
if (startIndex < closedParenIndex)
|
||||
{
|
||||
SimpleProcessToken(closedParenIndex - 1, normalizer);
|
||||
}
|
||||
|
||||
// Enforce a whitespace before the closing parenthesis
|
||||
TokenData td = PreviousTokenData(closedParenIndex);
|
||||
if (isNewlineRequired && !IsTokenWhitespace(td))
|
||||
{
|
||||
td = GetTokenData(closedParenIndex);
|
||||
AddIndentedNewLineReplacement(td.StartIndex);
|
||||
}
|
||||
}
|
||||
|
||||
protected int ProcessColumnList(int nextToken, SqlIdentifierCollection columnList, NormalizeWhitespace normalizer)
|
||||
{
|
||||
// find where the columns start
|
||||
IEnumerator<SqlIdentifier> columnEnum = columnList.GetEnumerator();
|
||||
if (columnEnum.MoveNext())
|
||||
{
|
||||
ProcessAndNormalizeWhitespaceRange(nextToken, columnEnum.Current.Position.startTokenNumber, normalizer);
|
||||
|
||||
ProcessChild(columnEnum.Current);
|
||||
SqlIdentifier previousColumn = columnEnum.Current;
|
||||
while (columnEnum.MoveNext())
|
||||
{
|
||||
CommaSeparatedList.ProcessInterChildRegion(previousColumn, columnEnum.Current);
|
||||
ProcessChild(columnEnum.Current);
|
||||
previousColumn = columnEnum.Current;
|
||||
}
|
||||
nextToken = previousColumn.Position.endTokenNumber;
|
||||
}
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
protected int ProcessAsToken(int nextToken, bool indentAfterAs)
|
||||
{
|
||||
int asTokenIndex = FindTokenWithId(nextToken, FormatterTokens.TOKEN_AS);
|
||||
|
||||
// Preprocess
|
||||
ProcessTokenRangeEnsuringOneNewLineMinumum(nextToken, asTokenIndex);
|
||||
|
||||
// Process As
|
||||
if (nextToken >= asTokenIndex
|
||||
|| !IsTokenWhitespace(PreviousTokenData(asTokenIndex)))
|
||||
{
|
||||
TokenData td = GetTokenData(asTokenIndex);
|
||||
AddIndentedNewLineReplacement(td.StartIndex);
|
||||
}
|
||||
ProcessTokenRange(asTokenIndex, asTokenIndex + 1);
|
||||
|
||||
if (indentAfterAs)
|
||||
{
|
||||
IncrementIndentLevel();
|
||||
}
|
||||
// Post Process
|
||||
nextToken = EnsureWhitespaceAfterAs(asTokenIndex);
|
||||
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
private int EnsureWhitespaceAfterAs(int asTokenIndex)
|
||||
{
|
||||
int nextToken = asTokenIndex + 1;
|
||||
Debug.Assert(nextToken < CodeObject.Position.endTokenNumber, "View definition ends unexpectedly after the AS token.");
|
||||
|
||||
TokenData nextTokenData = GetTokenData(nextToken);
|
||||
// Ensure a whitespace after the "AS" token
|
||||
if (!IsTokenWhitespace(nextTokenData))
|
||||
{
|
||||
AddIndentedNewLineReplacement(nextTokenData.StartIndex);
|
||||
}
|
||||
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for a set of utility formatters that are used by Node-specific formatters when dealing with whitespace
|
||||
/// </summary>
|
||||
internal abstract class WhiteSpaceSeparatedListFormatter : ASTNodeFormatterT<SqlCodeObject>
|
||||
{
|
||||
private bool IncremenetIndentLevelOnPrefixRegion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This constructor initalizes the <see cref="Visitor"/> and <see cref="CodeObject"/> properties since the formatter's entry point
|
||||
/// is not the Format method
|
||||
/// </summary>
|
||||
/// <param name="visitor"></param>
|
||||
/// <param name="codeObject"></param>
|
||||
/// <param name="incrementIndentLevelOnPrefixRegion"></param>
|
||||
internal WhiteSpaceSeparatedListFormatter(FormatterVisitor visitor, SqlCodeObject codeObject, bool incrementIndentLevelOnPrefixRegion)
|
||||
: base(visitor, codeObject)
|
||||
{
|
||||
IncremenetIndentLevelOnPrefixRegion = incrementIndentLevelOnPrefixRegion;
|
||||
}
|
||||
|
||||
internal override void ProcessPrefixRegion(int startTokenNumber, int firstChildStartTokenNumber)
|
||||
{
|
||||
if (IncremenetIndentLevelOnPrefixRegion)
|
||||
{
|
||||
IncrementIndentLevel();
|
||||
}
|
||||
base.ProcessPrefixRegion(startTokenNumber, firstChildStartTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessSuffixRegion(int lastChildEndTokenNumber, int endTokenNumber)
|
||||
{
|
||||
if (IncremenetIndentLevelOnPrefixRegion)
|
||||
{
|
||||
DecrementIndentLevel();
|
||||
}
|
||||
base.ProcessSuffixRegion(lastChildEndTokenNumber, endTokenNumber);
|
||||
}
|
||||
|
||||
internal override void ProcessInterChildRegion(SqlCodeObject previousChild, SqlCodeObject nextChild)
|
||||
{
|
||||
Validate.IsNotNull(nameof(previousChild), previousChild);
|
||||
Validate.IsNotNull(nameof(nextChild), nextChild);
|
||||
|
||||
int start = previousChild.Position.endTokenNumber;
|
||||
int end = nextChild.Position.startTokenNumber;
|
||||
|
||||
if (start < end)
|
||||
{
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
SimpleProcessToken(i, FormatWhitespace);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert the minimum whitespace
|
||||
string minWhite = FormatWhitespace(" ", Visitor.Context);
|
||||
int insertLocation = TokenManager.TokenList[start].StartIndex;
|
||||
AddReplacement(insertLocation, "", minWhite);
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract string FormatWhitespace(string original, FormatContext context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
//
|
||||
// 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.Composition;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
||||
using Microsoft.SqlTools.ServiceLayer.Extensibility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Formatter.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Formatter
|
||||
{
|
||||
|
||||
[Export(typeof(IHostedService))]
|
||||
public class TSqlFormatterService : HostedService<TSqlFormatterService>, IComposableService
|
||||
{
|
||||
private FormatterSettings settings;
|
||||
/// <summary>
|
||||
/// The default constructor is required for MEF-based composable services
|
||||
/// </summary>
|
||||
public TSqlFormatterService()
|
||||
{
|
||||
settings = new FormatterSettings();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void InitializeService(IProtocolEndpoint serviceHost)
|
||||
{
|
||||
serviceHost.SetRequestHandler(DocumentFormattingRequest.Type, HandleDocFormatRequest);
|
||||
serviceHost.SetRequestHandler(DocumentRangeFormattingRequest.Type, HandleDocRangeFormatRequest);
|
||||
|
||||
|
||||
|
||||
WorkspaceService?.RegisterConfigChangeCallback(HandleDidChangeConfigurationNotification);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the workspace service. Note: should handle case where this is null in cases where unit tests do not set this up
|
||||
/// </summary>
|
||||
private WorkspaceService<SqlToolsSettings> WorkspaceService
|
||||
{
|
||||
get { return ServiceProvider.GetService<WorkspaceService<SqlToolsSettings>>(); }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ensure formatter settings are always up to date
|
||||
/// </summary>
|
||||
public Task HandleDidChangeConfigurationNotification(
|
||||
SqlToolsSettings newSettings,
|
||||
SqlToolsSettings oldSettings,
|
||||
EventContext eventContext)
|
||||
{
|
||||
// update the current settings to reflect any changes (assuming formatter settings exist)
|
||||
settings = newSettings.SqlTools.Format ?? settings;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public async Task HandleDocFormatRequest(DocumentFormattingParams docFormatParams, RequestContext<TextEdit[]> requestContext)
|
||||
{
|
||||
Func<Task<TextEdit[]>> requestHandler = () =>
|
||||
{
|
||||
return FormatAndReturnEdits(docFormatParams);
|
||||
};
|
||||
await HandleRequest(requestHandler, requestContext, "HandleDocFormatRequest");
|
||||
}
|
||||
|
||||
public async Task HandleDocRangeFormatRequest(DocumentRangeFormattingParams docRangeFormatParams, RequestContext<TextEdit[]> requestContext)
|
||||
{
|
||||
Func<Task<TextEdit[]>> requestHandler = () =>
|
||||
{
|
||||
return FormatRangeAndReturnEdits(docRangeFormatParams);
|
||||
};
|
||||
await HandleRequest(requestHandler, requestContext, "HandleDocRangeFormatRequest");
|
||||
}
|
||||
|
||||
private async Task<TextEdit[]> FormatRangeAndReturnEdits(DocumentRangeFormattingParams docFormatParams)
|
||||
{
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
var range = docFormatParams.Range;
|
||||
ScriptFile scriptFile = GetFile(docFormatParams);
|
||||
TextEdit textEdit = new TextEdit { Range = range };
|
||||
string text = scriptFile.GetTextInRange(range.ToBufferRange());
|
||||
return DoFormat(docFormatParams, textEdit, text);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<TextEdit[]> FormatAndReturnEdits(DocumentFormattingParams docFormatParams)
|
||||
{
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
var scriptFile = GetFile(docFormatParams);
|
||||
if (scriptFile.FileLines.Count == 0)
|
||||
{
|
||||
return new TextEdit[0];
|
||||
}
|
||||
TextEdit textEdit = PrepareEdit(scriptFile);
|
||||
string text = scriptFile.Contents;
|
||||
return DoFormat(docFormatParams, textEdit, text);
|
||||
});
|
||||
}
|
||||
|
||||
private TextEdit[] DoFormat(DocumentFormattingParams docFormatParams, TextEdit edit, string text)
|
||||
{
|
||||
Validate.IsNotNull(nameof(docFormatParams), docFormatParams);
|
||||
|
||||
FormatOptions options = GetOptions(docFormatParams);
|
||||
List<TextEdit> edits = new List<TextEdit>();
|
||||
edit.NewText = Format(text, options, false);
|
||||
// TODO do not add if no formatting needed?
|
||||
edits.Add(edit);
|
||||
return edits.ToArray();
|
||||
}
|
||||
|
||||
private FormatOptions GetOptions(DocumentFormattingParams docFormatParams)
|
||||
{
|
||||
return MergeFormatOptions(docFormatParams.Options, settings);
|
||||
}
|
||||
|
||||
internal static FormatOptions MergeFormatOptions(FormattingOptions formatRequestOptions, FormatterSettings settings)
|
||||
|
||||
{
|
||||
FormatOptions options = new FormatOptions();
|
||||
if (formatRequestOptions != null)
|
||||
{
|
||||
options.UseSpaces = formatRequestOptions.InsertSpaces;
|
||||
options.SpacesPerIndent = formatRequestOptions.TabSize;
|
||||
}
|
||||
UpdateFormatOptionsFromSettings(options, settings);
|
||||
return options;
|
||||
|
||||
}
|
||||
|
||||
internal static void UpdateFormatOptionsFromSettings(FormatOptions options, FormatterSettings settings)
|
||||
{
|
||||
Validate.IsNotNull(nameof(options), options);
|
||||
if (settings != null)
|
||||
{
|
||||
if (settings.AlignColumnDefinitionsInColumns.HasValue) { options.AlignColumnDefinitionsInColumns = settings.AlignColumnDefinitionsInColumns.Value; }
|
||||
|
||||
if (settings.PlaceCommasBeforeNextStatement.HasValue) { options.PlaceCommasBeforeNextStatement = settings.PlaceCommasBeforeNextStatement.Value; }
|
||||
|
||||
if (settings.PlaceSelectStatementReferencesOnNewLine.HasValue) { options.PlaceEachReferenceOnNewLineInQueryStatements = settings.PlaceSelectStatementReferencesOnNewLine.Value; }
|
||||
|
||||
if (settings.UseBracketForIdentifiers.HasValue) { options.EncloseIdentifiersInSquareBrackets = settings.UseBracketForIdentifiers.Value; }
|
||||
|
||||
options.DatatypeCasing = settings.DatatypeCasing;
|
||||
options.KeywordCasing = settings.KeywordCasing;
|
||||
}
|
||||
}
|
||||
|
||||
private ScriptFile GetFile(DocumentFormattingParams docFormatParams)
|
||||
{
|
||||
return WorkspaceService.Workspace.GetFile(docFormatParams.TextDocument.Uri);
|
||||
}
|
||||
|
||||
private static TextEdit PrepareEdit(ScriptFile scriptFile)
|
||||
{
|
||||
int fileLines = scriptFile.FileLines.Count;
|
||||
Position start = new Position { Line = 0, Character = 0 };
|
||||
int lastChar = scriptFile.FileLines[scriptFile.FileLines.Count - 1].Length;
|
||||
Position end = new Position { Line = scriptFile.FileLines.Count - 1, Character = lastChar };
|
||||
|
||||
TextEdit edit = new TextEdit
|
||||
{
|
||||
Range = new Range { Start = start, End = end }
|
||||
};
|
||||
return edit;
|
||||
}
|
||||
|
||||
private async Task HandleRequest<T>(Func<Task<T>> handler, RequestContext<T> requestContext, string requestType)
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, requestType);
|
||||
|
||||
try
|
||||
{
|
||||
T result = await handler();
|
||||
await requestContext.SendResult(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await requestContext.SendError(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public string Format(TextReader input)
|
||||
{
|
||||
string originalSql = input.ReadToEnd();
|
||||
return Format(originalSql, new FormatOptions());
|
||||
}
|
||||
|
||||
public string Format(string input, FormatOptions options)
|
||||
{
|
||||
return Format(input, options, true);
|
||||
}
|
||||
|
||||
public string Format(string input, FormatOptions options, bool verifyOutput)
|
||||
{
|
||||
string result = null;
|
||||
DoFormat(input, options, verifyOutput, visitor =>
|
||||
{
|
||||
result = visitor.Context.FormattedSql;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Format(string input, FormatOptions options, bool verifyOutput, Replacement.OnReplace replace)
|
||||
{
|
||||
DoFormat(input, options, verifyOutput, visitor =>
|
||||
{
|
||||
foreach (Replacement r in visitor.Context.Replacements)
|
||||
{
|
||||
r.Apply(replace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void DoFormat(string input, FormatOptions options, bool verifyOutput, Action<FormatterVisitor> postFormatAction)
|
||||
{
|
||||
Validate.IsNotNull(nameof(input), input);
|
||||
Validate.IsNotNull(nameof(options), options);
|
||||
|
||||
ParseResult result = Parser.Parse(input);
|
||||
FormatContext context = new FormatContext(result.Script, options);
|
||||
|
||||
FormatterVisitor visitor = new FormatterVisitor(context, ServiceProvider);
|
||||
result.Script.Accept(visitor);
|
||||
if (verifyOutput)
|
||||
{
|
||||
visitor.VerifyFormat();
|
||||
}
|
||||
|
||||
postFormatAction?.Invoke(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class RangeExtensions
|
||||
{
|
||||
public static BufferRange ToBufferRange(this Range range)
|
||||
{
|
||||
// It turns out that VSCode sends Range objects as 0-indexed lines, while
|
||||
// our BufferPosition and BufferRange logic assumes 1-indexed. Therefore
|
||||
// need to increment all ranges by 1 when copying internally and reduce
|
||||
// when returning to the caller
|
||||
return new BufferRange(
|
||||
new BufferPosition(range.Start.Line + 1, range.Start.Character + 1),
|
||||
new BufferPosition(range.End.Line + 1, range.End.Character + 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
105
src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
Normal file
105
src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// 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.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Credentials;
|
||||
using Microsoft.SqlTools.ServiceLayer.Extensibility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides support for starting up a service host. This is a common responsibility
|
||||
/// for both the main service program and test driver that interacts with it
|
||||
/// </summary>
|
||||
public static class HostLoader
|
||||
{
|
||||
private static object lockObject = new object();
|
||||
private static bool isLoaded;
|
||||
|
||||
internal static ServiceHost CreateAndStartServiceHost(SqlToolsContext sqlToolsContext)
|
||||
{
|
||||
ServiceHost serviceHost = ServiceHost.Instance;
|
||||
lock (lockObject)
|
||||
{
|
||||
if (!isLoaded)
|
||||
{
|
||||
// Grab the instance of the service host
|
||||
serviceHost.Initialize();
|
||||
|
||||
InitializeRequestHandlersAndServices(serviceHost, sqlToolsContext);
|
||||
|
||||
// Start the service only after all request handlers are setup. This is vital
|
||||
// as otherwise the Initialize event can be lost - it's processed and discarded before the handler
|
||||
// is hooked up to receive the message
|
||||
serviceHost.Start().Wait();
|
||||
isLoaded = true;
|
||||
}
|
||||
}
|
||||
return serviceHost;
|
||||
}
|
||||
|
||||
private static void InitializeRequestHandlersAndServices(ServiceHost serviceHost, SqlToolsContext sqlToolsContext)
|
||||
{
|
||||
// Load extension provider, which currently finds all exports in current DLL. Can be changed to find based
|
||||
// on directory or assembly list quite easily in the future
|
||||
ExtensionServiceProvider serviceProvider = ExtensionServiceProvider.CreateDefaultServiceProvider();
|
||||
serviceProvider.RegisterSingleService(sqlToolsContext);
|
||||
serviceProvider.RegisterSingleService(serviceHost);
|
||||
|
||||
// Initialize and register singleton services so they're accessible for any MEF service. In the future, these
|
||||
// could be updated to be IComposableServices, which would avoid the requirement to define a singleton instance
|
||||
// and instead have MEF handle discovery & loading
|
||||
WorkspaceService<SqlToolsSettings>.Instance.InitializeService(serviceHost);
|
||||
serviceProvider.RegisterSingleService(WorkspaceService<SqlToolsSettings>.Instance);
|
||||
|
||||
LanguageService.Instance.InitializeService(serviceHost, sqlToolsContext);
|
||||
serviceProvider.RegisterSingleService(LanguageService.Instance);
|
||||
|
||||
ConnectionService.Instance.InitializeService(serviceHost);
|
||||
serviceProvider.RegisterSingleService(ConnectionService.Instance);
|
||||
|
||||
CredentialService.Instance.InitializeService(serviceHost);
|
||||
serviceProvider.RegisterSingleService(CredentialService.Instance);
|
||||
|
||||
QueryExecutionService.Instance.InitializeService(serviceHost);
|
||||
serviceProvider.RegisterSingleService(QueryExecutionService.Instance);
|
||||
|
||||
InitializeHostedServices(serviceProvider, serviceHost);
|
||||
|
||||
serviceHost.InitializeRequestHandlers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal to support testing. Initializes <see cref="IHostedService"/> instances in the service,
|
||||
/// and registers them for their preferred service type
|
||||
/// </summary>
|
||||
internal static void InitializeHostedServices(RegisteredServiceProvider provider, IProtocolEndpoint host)
|
||||
{
|
||||
// Pre-register all services before initializing. This ensures that if one service wishes to reference
|
||||
// another one during initialization, it will be able to safely do so
|
||||
foreach (IHostedService service in provider.GetServices<IHostedService>())
|
||||
{
|
||||
provider.RegisterSingleService(service.ServiceType, service);
|
||||
}
|
||||
|
||||
foreach (IHostedService service in provider.GetServices<IHostedService>())
|
||||
{
|
||||
// Initialize all hosted services, and register them in the service provider for their requested
|
||||
// service type. This ensures that when searching for the ConnectionService you can get it without
|
||||
// searching for an IHostedService of type ConnectionService
|
||||
service.InitializeService(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Contracts
|
||||
|
||||
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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
/// </summary>
|
||||
public class ProtocolEndpoint : IProtocolEndpoint
|
||||
{
|
||||
private bool isInitialized;
|
||||
private bool isStarted;
|
||||
private int currentMessageId;
|
||||
private ChannelBase protocolChannel;
|
||||
@@ -64,12 +65,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the language server client and sends the Initialize method.
|
||||
/// Initializes
|
||||
/// </summary>
|
||||
/// <returns>A Task that can be awaited for initialization to complete.</returns>
|
||||
public async Task Start()
|
||||
public void Initialize()
|
||||
{
|
||||
if (!this.isStarted)
|
||||
if (!this.isInitialized)
|
||||
{
|
||||
// Start the provided protocol channel
|
||||
this.protocolChannel.Start(this.messageProtocolType);
|
||||
@@ -83,6 +83,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||
// 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();
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
|
||||
/// <summary>
|
||||
/// Provide initialization that must occur after the service host is started
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
public void InitializeRequestHandlers()
|
||||
{
|
||||
// Register the requests that this service host will handle
|
||||
this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
|
||||
@@ -153,6 +153,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
|
||||
TextDocumentSync = TextDocumentSyncKind.Incremental,
|
||||
DefinitionProvider = true,
|
||||
ReferencesProvider = false,
|
||||
DocumentFormattingProvider = true,
|
||||
DocumentRangeFormattingProvider = true,
|
||||
DocumentHighlightProvider = false,
|
||||
HoverProvider = true,
|
||||
CompletionProvider = new CompletionOptions
|
||||
|
||||
@@ -123,6 +123,28 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the binding queue entry
|
||||
/// </summary>
|
||||
protected void RemoveBindingContext(string key)
|
||||
{
|
||||
lock (this.bindingContextLock)
|
||||
{
|
||||
if (this.BindingContextMap.ContainsKey(key))
|
||||
{
|
||||
// disconnect existing connection
|
||||
var bindingContext = this.BindingContextMap[key];
|
||||
if (bindingContext.ServerConnection != null && bindingContext.ServerConnection.IsOpen)
|
||||
{
|
||||
bindingContext.ServerConnection.Disconnect();
|
||||
}
|
||||
|
||||
// remove key from the map
|
||||
this.BindingContextMap.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasPendingQueueItems
|
||||
{
|
||||
get
|
||||
|
||||
@@ -51,8 +51,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
/// <summary>
|
||||
/// Use a ConnectionInfo item to create a connected binding context
|
||||
/// </summary>
|
||||
/// <param name="connInfo"></param>
|
||||
public virtual string AddConnectionContext(ConnectionInfo connInfo)
|
||||
/// <param name="connInfo">Connection info used to create binding context</param>
|
||||
/// <param name="overwrite">Overwrite existing context</param>
|
||||
public virtual string AddConnectionContext(ConnectionInfo connInfo, bool overwrite = false)
|
||||
{
|
||||
if (connInfo == null)
|
||||
{
|
||||
@@ -63,8 +64,15 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
string connectionKey = GetConnectionContextKey(connInfo);
|
||||
if (BindingContextExists(connectionKey))
|
||||
{
|
||||
// no need to populate the context again since the context already exists
|
||||
return connectionKey;
|
||||
if (overwrite)
|
||||
{
|
||||
RemoveBindingContext(connectionKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no need to populate the context again since the context already exists
|
||||
return connectionKey;
|
||||
}
|
||||
}
|
||||
IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey);
|
||||
|
||||
|
||||
@@ -45,14 +45,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||
Reference = 18
|
||||
}
|
||||
|
||||
[DebuggerDisplay("NewText = {NewText}, Range = {Range.Start.Line}:{Range.Start.Character} - {Range.End.Line}:{Range.End.Character}")]
|
||||
public class TextEdit
|
||||
{
|
||||
public Range Range { get; set; }
|
||||
|
||||
public string NewText { get; set; }
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Kind = {Kind.ToString()}, Label = {Label}, Detail = {Detail}")]
|
||||
public class CompletionItem
|
||||
{
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters to be sent back with a rebuild IntelliSense event
|
||||
/// </summary>
|
||||
public class RebuildIntelliSenseParams
|
||||
{
|
||||
/// <summary>
|
||||
/// URI identifying the file that should have its IntelliSense cache rebuilt
|
||||
/// </summary>
|
||||
public string OwnerUri { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RebuildIntelliSenseNotification notification mapping entry
|
||||
/// </summary>
|
||||
public class RebuildIntelliSenseNotification
|
||||
{
|
||||
public static readonly
|
||||
EventType<RebuildIntelliSenseParams> Type =
|
||||
EventType<RebuildIntelliSenseParams>.Create("textDocument/rebuildIntelliSense");
|
||||
}
|
||||
}
|
||||
@@ -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 System.Diagnostics;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||
{
|
||||
|
||||
[DebuggerDisplay("NewText = {NewText}, Range = {Range.Start.Line}:{Range.Start.Character} - {Range.End.Line}:{Range.End.Character}")]
|
||||
public class TextEdit
|
||||
{
|
||||
public Range Range { get; set; }
|
||||
|
||||
public string NewText { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -212,6 +212,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
serviceHost.SetRequestHandler(HoverRequest.Type, HandleHoverRequest);
|
||||
serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
|
||||
serviceHost.SetRequestHandler(DefinitionRequest.Type, HandleDefinitionRequest);
|
||||
serviceHost.SetEventHandler(RebuildIntelliSenseNotification.Type, HandleRebuildIntelliSenseNotification);
|
||||
|
||||
// Register a no-op shutdown task for validation of the shutdown logic
|
||||
serviceHost.RegisterShutdownTask(async (shutdownParams, shutdownRequestContext) =>
|
||||
@@ -442,6 +443,62 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
await Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the rebuild IntelliSense cache notification
|
||||
/// </summary>
|
||||
public async Task HandleRebuildIntelliSenseNotification(
|
||||
RebuildIntelliSenseParams configChangeParams,
|
||||
EventContext eventContext)
|
||||
{
|
||||
Logger.Write(LogLevel.Verbose, "HandleRebuildIntelliSenseNotification");
|
||||
|
||||
// Skip closing this file if the file doesn't exist
|
||||
var scriptFile = this.CurrentWorkspace.GetFile(configChangeParams.OwnerUri);
|
||||
if (scriptFile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectionInfo connInfo;
|
||||
LanguageService.ConnectionServiceInstance.TryFindConnection(
|
||||
scriptFile.ClientFilePath,
|
||||
out connInfo);
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
ScriptParseInfo scriptInfo = GetScriptParseInfo(connInfo.OwnerUri, createIfNotExists: false);
|
||||
if (scriptInfo != null && scriptInfo.IsConnected &&
|
||||
Monitor.TryEnter(scriptInfo.BuildingMetadataLock, LanguageService.OnConnectionWaitTimeout))
|
||||
{
|
||||
try
|
||||
{
|
||||
this.BindingQueue.AddConnectionContext(connInfo, overwrite: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Set Metadata Build event to Signal state.
|
||||
Monitor.Exit(scriptInfo.BuildingMetadataLock);
|
||||
}
|
||||
}
|
||||
|
||||
// if not in the preview window and diagnostics are enabled then run diagnostics
|
||||
if (!IsPreviewWindow(scriptFile)
|
||||
&& WorkspaceService<SqlToolsSettings>.Instance.CurrentSettings.IsDiagnositicsEnabled)
|
||||
{
|
||||
RunScriptDiagnostics(
|
||||
new ScriptFile[] { scriptFile },
|
||||
eventContext);
|
||||
}
|
||||
|
||||
// Send a notification to signal that autocomplete is ready
|
||||
ServiceHost.Instance.SendEvent(IntelliSenseReadyNotification.Type, new IntelliSenseReadyParams() {OwnerUri = connInfo.OwnerUri});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the file configuration change notification
|
||||
/// </summary>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer {
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Localization {
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Microsoft.SqlTools.ServiceLayer {
|
||||
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.sr", typeof(sr).GetTypeInfo().Assembly);
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.SqlTools.ServiceLayer.Localization.sr", typeof(sr).GetTypeInfo().Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
@@ -446,6 +446,24 @@ namespace Microsoft.SqlTools.ServiceLayer {
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
@@ -482,6 +500,24 @@ namespace Microsoft.SqlTools.ServiceLayer {
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
@@ -833,6 +869,42 @@ namespace Microsoft.SqlTools.ServiceLayer {
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
@@ -117,6 +117,62 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
}
|
||||
}
|
||||
|
||||
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 ErrorUnexpectedCodeObjectType
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.ErrorUnexpectedCodeObjectType);
|
||||
}
|
||||
}
|
||||
|
||||
public static string HostingUnexpectedEndOfStream
|
||||
{
|
||||
get
|
||||
@@ -413,6 +469,14 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
}
|
||||
}
|
||||
|
||||
public static string ErrorEmptyStringReplacement
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.ErrorEmptyStringReplacement);
|
||||
}
|
||||
}
|
||||
|
||||
public static string WorkspaceServicePositionLineOutOfRange
|
||||
{
|
||||
get
|
||||
@@ -724,7 +788,7 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Keys
|
||||
{
|
||||
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ServiceLayer.SR", typeof(SR).GetTypeInfo().Assembly);
|
||||
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ServiceLayer.Localization.SR", typeof(SR).GetTypeInfo().Assembly);
|
||||
|
||||
static CultureInfo _culture = null;
|
||||
|
||||
@@ -774,6 +838,27 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
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 ErrorUnexpectedCodeObjectType = "ErrorUnexpectedCodeObjectType";
|
||||
|
||||
|
||||
public const string HostingUnexpectedEndOfStream = "HostingUnexpectedEndOfStream";
|
||||
|
||||
|
||||
@@ -903,6 +988,9 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
public const string PeekDefinitionTypeNotSupportedError = "PeekDefinitionTypeNotSupportedError";
|
||||
|
||||
|
||||
public const string ErrorEmptyStringReplacement = "ErrorEmptyStringReplacement";
|
||||
|
||||
|
||||
public const string WorkspaceServicePositionLineOutOfRange = "WorkspaceServicePositionLineOutOfRange";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@@ -181,6 +181,34 @@
|
||||
<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="ErrorUnexpectedCodeObjectType" xml:space="preserve">
|
||||
<value>Cannot convert SqlCodeObject Type {0} to Type {1}</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="HostingUnexpectedEndOfStream" xml:space="preserve">
|
||||
<value>MessageReader's input stream ended unexpectedly, terminating</value>
|
||||
<comment></comment>
|
||||
@@ -359,6 +387,10 @@
|
||||
<value>This object type is currently not supported by this feature.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="ErrorEmptyStringReplacement" xml:space="preserve">
|
||||
<value>Replacement of an empty string by an empty string.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="WorkspaceServicePositionLineOutOfRange" xml:space="preserve">
|
||||
<value>Position is outside of file line range</value>
|
||||
<comment></comment>
|
||||
@@ -59,6 +59,26 @@ CredentialsServiceTargetForLookup = Target must be specified to check existance
|
||||
|
||||
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}
|
||||
|
||||
############################################################################
|
||||
# Formatter
|
||||
|
||||
ErrorUnexpectedCodeObjectType = Cannot convert SqlCodeObject Type {0} to Type {1}
|
||||
|
||||
############################################################################
|
||||
# Hosting
|
||||
|
||||
@@ -168,6 +188,8 @@ PeekDefinitionTimedoutError = Operation timed out.
|
||||
|
||||
PeekDefinitionTypeNotSupportedError = This object type is currently not supported by this feature.
|
||||
|
||||
ErrorEmptyStringReplacement = Replacement of an empty string by an empty string.
|
||||
|
||||
############################################################################
|
||||
# Workspace Service
|
||||
|
||||
514
src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
Normal file
514
src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
Normal file
@@ -0,0 +1,514 @@
|
||||
<?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="ConnectionServiceConnectErrorNullParams">
|
||||
<source>Connection parameters cannot be null</source>
|
||||
<target state="new">Connection parameters cannot be null</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ConnectionServiceListDbErrorNullOwnerUri">
|
||||
<source>OwnerUri cannot be null or empty</source>
|
||||
<target state="new">OwnerUri cannot be null or empty</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ConnectionServiceListDbErrorNotConnected">
|
||||
<source>SpecifiedUri '{0}' does not have existing connection</source>
|
||||
<target state="new">SpecifiedUri '{0}' does not have existing connection</target>
|
||||
<note>.
|
||||
Parameters: 0 - uri (string) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ConnectionServiceConnStringInvalidAuthType">
|
||||
<source>Invalid value '{0}' for AuthenticationType. Valid values are 'Integrated' and 'SqlLogin'.</source>
|
||||
<target state="new">Invalid value '{0}' for AuthenticationType. Valid values are 'Integrated' and 'SqlLogin'.</target>
|
||||
<note>.
|
||||
Parameters: 0 - authType (string) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ConnectionServiceConnStringInvalidIntent">
|
||||
<source>Invalid value '{0}' for ApplicationIntent. Valid values are 'ReadWrite' and 'ReadOnly'.</source>
|
||||
<target state="new">Invalid value '{0}' for ApplicationIntent. Valid values are 'ReadWrite' and 'ReadOnly'.</target>
|
||||
<note>.
|
||||
Parameters: 0 - intent (string) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ConnectionServiceConnectionCanceled">
|
||||
<source>Connection canceled</source>
|
||||
<target state="new">Connection canceled</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ConnectionParamsValidateNullOwnerUri">
|
||||
<source>OwnerUri cannot be null or empty</source>
|
||||
<target state="new">OwnerUri cannot be null or empty</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ConnectionParamsValidateNullConnection">
|
||||
<source>Connection details object cannot be null</source>
|
||||
<target state="new">Connection details object cannot be null</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ConnectionParamsValidateNullServerName">
|
||||
<source>ServerName cannot be null or empty</source>
|
||||
<target state="new">ServerName cannot be null or empty</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ConnectionParamsValidateNullSqlAuth">
|
||||
<source>{0} cannot be null or empty when using SqlLogin authentication</source>
|
||||
<target state="new">{0} cannot be null or empty when using SqlLogin authentication</target>
|
||||
<note>.
|
||||
Parameters: 0 - component (string) </note>
|
||||
</trans-unit>
|
||||
<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="QueryServiceCancelAlreadyCompleted">
|
||||
<source>The query has already completed, it cannot be cancelled</source>
|
||||
<target state="new">The query has already completed, it cannot be cancelled</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceCancelDisposeFailed">
|
||||
<source>Query successfully cancelled, failed to dispose query. Owner URI not found.</source>
|
||||
<target state="new">Query successfully cancelled, failed to dispose query. Owner URI not found.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceQueryCancelled">
|
||||
<source>Query was canceled by user</source>
|
||||
<target state="new">Query was canceled by user</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceSubsetBatchNotCompleted">
|
||||
<source>The batch has not completed, yet</source>
|
||||
<target state="new">The batch has not completed, yet</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceSubsetBatchOutOfRange">
|
||||
<source>Batch index cannot be less than 0 or greater than the number of batches</source>
|
||||
<target state="new">Batch index cannot be less than 0 or greater than the number of batches</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceSubsetResultSetOutOfRange">
|
||||
<source>Result set index cannot be less than 0 or greater than the number of result sets</source>
|
||||
<target state="new">Result set index cannot be less than 0 or greater than the number of result sets</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceDataReaderByteCountInvalid">
|
||||
<source>Maximum number of bytes to return must be greater than zero</source>
|
||||
<target state="new">Maximum number of bytes to return must be greater than zero</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceDataReaderCharCountInvalid">
|
||||
<source>Maximum number of chars to return must be greater than zero</source>
|
||||
<target state="new">Maximum number of chars to return must be greater than zero</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceDataReaderXmlCountInvalid">
|
||||
<source>Maximum number of XML bytes to return must be greater than zero</source>
|
||||
<target state="new">Maximum number of XML bytes to return must be greater than zero</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceFileWrapperWriteOnly">
|
||||
<source>Access method cannot be write-only</source>
|
||||
<target state="new">Access method cannot be write-only</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceFileWrapperNotInitialized">
|
||||
<source>FileStreamWrapper must be initialized before performing operations</source>
|
||||
<target state="new">FileStreamWrapper must be initialized before performing operations</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceFileWrapperReadOnly">
|
||||
<source>This FileStreamWrapper cannot be used for writing</source>
|
||||
<target state="new">This FileStreamWrapper cannot be used for writing</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceAffectedOneRow">
|
||||
<source>(1 row affected)</source>
|
||||
<target state="new">(1 row affected)</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceAffectedRows">
|
||||
<source>({0} rows affected)</source>
|
||||
<target state="new">({0} rows affected)</target>
|
||||
<note>.
|
||||
Parameters: 0 - rows (long) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceCompletedSuccessfully">
|
||||
<source>Commands completed successfully.</source>
|
||||
<target state="new">Commands completed successfully.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceErrorFormat">
|
||||
<source>Msg {0}, Level {1}, State {2}, Line {3}{4}{5}</source>
|
||||
<target state="new">Msg {0}, Level {1}, State {2}, Line {3}{4}{5}</target>
|
||||
<note>.
|
||||
Parameters: 0 - msg (int), 1 - lvl (int), 2 - state (int), 3 - line (int), 4 - newLine (string), 5 - message (string) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceQueryFailed">
|
||||
<source>Query failed: {0}</source>
|
||||
<target state="new">Query failed: {0}</target>
|
||||
<note>.
|
||||
Parameters: 0 - message (string) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceColumnNull">
|
||||
<source>(No column name)</source>
|
||||
<target state="new">(No column name)</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceRequestsNoQuery">
|
||||
<source>The requested query does not exist</source>
|
||||
<target state="new">The requested query does not exist</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceQueryInvalidOwnerUri">
|
||||
<source>This editor is not connected to a database</source>
|
||||
<target state="new">This editor is not connected to a database</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceQueryInProgress">
|
||||
<source>A query is already in progress for this editor session. Please cancel this query or wait for its completion.</source>
|
||||
<target state="new">A query is already in progress for this editor session. Please cancel this query or wait for its completion.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceMessageSenderNotSql">
|
||||
<source>Sender for OnInfoMessage event must be a SqlConnection</source>
|
||||
<target state="new">Sender for OnInfoMessage event must be a SqlConnection</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceResultSetReaderNull">
|
||||
<source>Reader cannot be null</source>
|
||||
<target state="new">Reader cannot be null</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceSaveAsResultSetNotComplete">
|
||||
<source>Result cannot be saved until query execution has completed</source>
|
||||
<target state="new">Result cannot be saved until query execution has completed</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceSaveAsMiscStartingError">
|
||||
<source>Internal error occurred while starting save task</source>
|
||||
<target state="new">Internal error occurred while starting save task</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceSaveAsInProgress">
|
||||
<source>A save request to the same path is in progress</source>
|
||||
<target state="new">A save request to the same path is in progress</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceSaveAsFail">
|
||||
<source>Failed to save {0}: {1}</source>
|
||||
<target state="new">Failed to save {0}: {1}</target>
|
||||
<note>.
|
||||
Parameters: 0 - fileName (string), 1 - message (string) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceResultSetNotRead">
|
||||
<source>Cannot read subset unless the results have been read from the server</source>
|
||||
<target state="new">Cannot read subset unless the results have been read from the server</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceResultSetStartRowOutOfRange">
|
||||
<source>Start row cannot be less than 0 or greater than the number of rows in the result set</source>
|
||||
<target state="new">Start row cannot be less than 0 or greater than the number of rows in the result set</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceResultSetRowCountOutOfRange">
|
||||
<source>Row count must be a positive integer</source>
|
||||
<target state="new">Row count must be a positive integer</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceResultSetNoColumnSchema">
|
||||
<source>Could not retrieve column schema for result set</source>
|
||||
<target state="new">Could not retrieve column schema for result set</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="QueryServiceExecutionPlanNotFound">
|
||||
<source>Could not retrieve an execution plan from the result set </source>
|
||||
<target state="new">Could not retrieve an execution plan from the result set </target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PeekDefinitionAzureError">
|
||||
<source>This feature is currently not supported on Azure SQL DB and Data Warehouse: {0}</source>
|
||||
<target state="new">This feature is currently not supported on Azure SQL DB and Data Warehouse: {0}</target>
|
||||
<note>.
|
||||
Parameters: 0 - errorMessage (string) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PeekDefinitionError">
|
||||
<source>An unexpected error occurred during Peek Definition execution: {0}</source>
|
||||
<target state="new">An unexpected error occurred during Peek Definition execution: {0}</target>
|
||||
<note>.
|
||||
Parameters: 0 - errorMessage (string) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PeekDefinitionNoResultsError">
|
||||
<source>No results were found.</source>
|
||||
<target state="new">No results were found.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PeekDefinitionDatabaseError">
|
||||
<source>No database object was retrieved.</source>
|
||||
<target state="new">No database object was retrieved.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PeekDefinitionNotConnectedError">
|
||||
<source>Please connect to a server.</source>
|
||||
<target state="new">Please connect to a server.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PeekDefinitionTimedoutError">
|
||||
<source>Operation timed out.</source>
|
||||
<target state="new">Operation timed out.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PeekDefinitionTypeNotSupportedError">
|
||||
<source>This object type is currently not supported by this feature.</source>
|
||||
<target state="new">This object type is currently not supported by this feature.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WorkspaceServicePositionLineOutOfRange">
|
||||
<source>Position is outside of file line range</source>
|
||||
<target state="new">Position is outside of file line range</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WorkspaceServicePositionColumnOutOfRange">
|
||||
<source>Position is outside of column range for line {0}</source>
|
||||
<target state="new">Position is outside of column range for line {0}</target>
|
||||
<note>.
|
||||
Parameters: 0 - line (int) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WorkspaceServiceBufferPositionOutOfOrder">
|
||||
<source>Start position ({0}, {1}) must come before or be equal to the end position ({2}, {3})</source>
|
||||
<target state="new">Start position ({0}, {1}) must come before or be equal to the end position ({2}, {3})</target>
|
||||
<note>.
|
||||
Parameters: 0 - sLine (int), 1 - sCol (int), 2 - eLine (int), 3 - eCol (int) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_BatchSqlMessageNoProcedureInfo">
|
||||
<source>Msg {0}, Level {1}, State {2}, Line {3}</source>
|
||||
<target state="new">Msg {0}, Level {1}, State {2}, Line {3}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_BatchSqlMessageWithProcedureInfo">
|
||||
<source>Msg {0}, Level {1}, State {2}, Procedure {3}, Line {4}</source>
|
||||
<target state="new">Msg {0}, Level {1}, State {2}, Procedure {3}, Line {4}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_BatchSqlMessageNoLineInfo">
|
||||
<source>Msg {0}, Level {1}, State {2}</source>
|
||||
<target state="new">Msg {0}, Level {1}, State {2}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_BatchError_Exception">
|
||||
<source>An error occurred while the batch was being processed. The error message is: {0}</source>
|
||||
<target state="new">An error occurred while the batch was being processed. The error message is: {0}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_BatchExecutionInfo_RowsAffected">
|
||||
<source>({0} row(s) affected)</source>
|
||||
<target state="new">({0} row(s) affected)</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_ExecutionNotYetCompleteError">
|
||||
<source>The previous execution is not yet complete.</source>
|
||||
<target state="new">The previous execution is not yet complete.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_ScriptError_Error">
|
||||
<source>A scripting error occurred.</source>
|
||||
<target state="new">A scripting error occurred.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_ScriptError_ParsingSyntax">
|
||||
<source>Incorrect syntax was encountered while {0} was being parsed.</source>
|
||||
<target state="new">Incorrect syntax was encountered while {0} was being parsed.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_ScriptError_FatalError">
|
||||
<source>A fatal error occurred.</source>
|
||||
<target state="new">A fatal error occurred.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_ExecutionInfo_FinalizingLoop">
|
||||
<source>Execution completed {0} times...</source>
|
||||
<target state="new">Execution completed {0} times...</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_ExecutionInfo_QueryCancelledbyUser">
|
||||
<source>You cancelled the query.</source>
|
||||
<target state="new">You cancelled the query.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_BatchExecutionError_Halting">
|
||||
<source>An error occurred while the batch was being executed.</source>
|
||||
<target state="new">An error occurred while the batch was being executed.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_BatchExecutionError_Ignoring">
|
||||
<source>An error occurred while the batch was being executed, but the error has been ignored.</source>
|
||||
<target state="new">An error occurred while the batch was being executed, but the error has been ignored.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
|
||||
<source>Starting execution loop of {0} times...</source>
|
||||
<target state="new">Starting execution loop of {0} times...</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_ExecutionError_CommandNotSupported">
|
||||
<source>Command {0} is not supported.</source>
|
||||
<target state="new">Command {0} is not supported.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_ExecutionError_VariableNotFound">
|
||||
<source>The variable {0} could not be found.</source>
|
||||
<target state="new">The variable {0} could not be found.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParserWrapperExecutionEngineError">
|
||||
<source>SQL Execution error: {0}</source>
|
||||
<target state="new">SQL Execution error: {0}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParserWrapperExecutionError">
|
||||
<source>Batch parser wrapper execution: {0} found... at line {1}: {2} Description: {3}</source>
|
||||
<target state="new">Batch parser wrapper execution: {0} found... at line {1}: {2} Description: {3}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParserWrapperExecutionEngineBatchMessage">
|
||||
<source>Batch parser wrapper execution engine batch message received: Message: {0} Detailed message: {1}</source>
|
||||
<target state="new">Batch parser wrapper execution engine batch message received: Message: {0} Detailed message: {1}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParserWrapperExecutionEngineBatchResultSetProcessing">
|
||||
<source>Batch parser wrapper execution engine batch ResultSet processing: DataReader.FieldCount: {0} DataReader.RecordsAffected: {1}</source>
|
||||
<target state="new">Batch parser wrapper execution engine batch ResultSet processing: DataReader.FieldCount: {0} DataReader.RecordsAffected: {1}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParserWrapperExecutionEngineBatchResultSetFinished">
|
||||
<source>Batch parser wrapper execution engine batch ResultSet finished.</source>
|
||||
<target state="new">Batch parser wrapper execution engine batch ResultSet finished.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParserWrapperExecutionEngineBatchCancelling">
|
||||
<source>Canceling batch parser wrapper batch execution.</source>
|
||||
<target state="new">Canceling batch parser wrapper batch execution.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EE_ScriptError_Warning">
|
||||
<source>Scripting warning.</source>
|
||||
<target state="new">Scripting warning.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="TroubleshootingAssistanceMessage">
|
||||
<source>For more information about this error, see the troubleshooting topics in the product documentation.</source>
|
||||
<target state="new">For more information about this error, see the troubleshooting topics in the product documentation.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParser_CircularReference">
|
||||
<source>File '{0}' recursively included.</source>
|
||||
<target state="new">File '{0}' recursively included.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParser_CommentNotTerminated">
|
||||
<source>Missing end comment mark '*/'.</source>
|
||||
<target state="new">Missing end comment mark '*/'.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParser_StringNotTerminated">
|
||||
<source>Unclosed quotation mark after the character string.</source>
|
||||
<target state="new">Unclosed quotation mark after the character string.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParser_IncorrectSyntax">
|
||||
<source>Incorrect syntax was encountered while parsing '{0}'.</source>
|
||||
<target state="new">Incorrect syntax was encountered while parsing '{0}'.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BatchParser_VariableNotDefined">
|
||||
<source>Variable {0} is not defined.</source>
|
||||
<target state="new">Variable {0} is not defined.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="TestLocalizationConstant">
|
||||
<source>EN_LOCALIZATION</source>
|
||||
<target state="new">EN_LOCALIZATION</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>
|
||||
<trans-unit id="ErrorUnexpectedCodeObjectType">
|
||||
<source>Cannot convert SqlCodeObject Type {0} to Type {1}</source>
|
||||
<target state="new">Cannot convert SqlCodeObject Type {0} to Type {1}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ErrorEmptyStringReplacement">
|
||||
<source>Replacement of an empty string by an empty string.</source>
|
||||
<target state="new">Replacement of an empty string by an empty string.</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>
|
||||
@@ -4,11 +4,6 @@
|
||||
using System;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.Credentials;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer
|
||||
@@ -36,25 +31,13 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
Logger.Write(LogLevel.Normal, "Starting SQL Tools Service Host");
|
||||
|
||||
// set up the host details and profile paths
|
||||
var hostDetails = new HostDetails(version: new Version(1,0));
|
||||
var hostDetails = new HostDetails(version: new Version(1, 0));
|
||||
|
||||
SqlToolsContext sqlToolsContext = new SqlToolsContext(hostDetails);
|
||||
ServiceHost serviceHost = HostLoader.CreateAndStartServiceHost(sqlToolsContext);
|
||||
|
||||
// Grab the instance of the service host
|
||||
ServiceHost serviceHost = ServiceHost.Instance;
|
||||
|
||||
// Start the service
|
||||
serviceHost.Start().Wait();
|
||||
|
||||
// Initialize the services that will be hosted here
|
||||
WorkspaceService<SqlToolsSettings>.Instance.InitializeService(serviceHost);
|
||||
LanguageService.Instance.InitializeService(serviceHost, sqlToolsContext);
|
||||
ConnectionService.Instance.InitializeService(serviceHost);
|
||||
CredentialService.Instance.InitializeService(serviceHost);
|
||||
QueryExecutionService.Instance.InitializeService(serviceHost);
|
||||
|
||||
serviceHost.Initialize();
|
||||
serviceHost.WaitForExit();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// 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.Formatter;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.SqlContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Contract for receiving formatter-specific settings as part of workspace settings
|
||||
/// </summary>
|
||||
public class FormatterSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Should names be escaped, for example converting dbo.T1 to [dbo].[T1]
|
||||
/// </summary>
|
||||
public bool? UseBracketForIdentifiers
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should comma separated lists have the comma be at the start of a new line.
|
||||
/// <code>
|
||||
/// CREATE TABLE T1 (
|
||||
/// C1 INT
|
||||
/// , C2 INT)
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public bool? PlaceCommasBeforeNextStatement
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should each reference be on its own line or should references to multiple objects
|
||||
/// be kept on a single line
|
||||
/// <code>
|
||||
/// SELECT *
|
||||
/// FROM T1,
|
||||
/// T2
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public bool? PlaceSelectStatementReferencesOnNewLine
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should keyword casing be ignored, converted to all uppercase, or
|
||||
/// converted to all lowercase
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public CasingOptions KeywordCasing
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Should data type casing be ignored, converted to all uppercase, or
|
||||
/// converted to all lowercase
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public CasingOptions DatatypeCasing
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Should column definitions be aligned or left non-aligned?
|
||||
/// </summary>
|
||||
public bool? AlignColumnDefinitionsInColumns
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,9 +113,9 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlContext
|
||||
/// </summary>
|
||||
public SqlToolsSettingsValues()
|
||||
{
|
||||
|
||||
this.IntelliSense = new IntelliSenseSettings();
|
||||
this.QueryExecutionSettings = new QueryExecutionSettings();
|
||||
IntelliSense = new IntelliSenseSettings();
|
||||
QueryExecutionSettings = new QueryExecutionSettings();
|
||||
Format = new FormatterSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -127,5 +127,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlContext
|
||||
/// Gets or sets the query execution settings
|
||||
/// </summary>
|
||||
public QueryExecutionSettings QueryExecutionSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the formatter settings
|
||||
/// </summary>
|
||||
[JsonProperty("format")]
|
||||
public FormatterSettings Format { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
/// <returns>A new FilePosition instance for the calculated position.</returns>
|
||||
public FilePosition AddOffset(int lineOffset, int columnOffset)
|
||||
{
|
||||
return this.scriptFile.CalculatePosition(
|
||||
return scriptFile.CalculatePosition(
|
||||
this,
|
||||
lineOffset,
|
||||
columnOffset);
|
||||
@@ -77,7 +77,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
/// <returns>A new FilePosition instance for the calculated position.</returns>
|
||||
public FilePosition GetLineStart()
|
||||
{
|
||||
string scriptLine = scriptFile.FileLines[this.Line - 1];
|
||||
string scriptLine = scriptFile.FileLines[Line - 1];
|
||||
|
||||
int lineStartColumn = 1;
|
||||
for (int i = 0; i < scriptLine.Length; i++)
|
||||
@@ -89,7 +89,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
}
|
||||
}
|
||||
|
||||
return new FilePosition(this.scriptFile, this.Line, lineStartColumn);
|
||||
return new FilePosition(scriptFile, Line, lineStartColumn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -99,8 +99,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
/// <returns>A new FilePosition instance for the calculated position.</returns>
|
||||
public FilePosition GetLineEnd()
|
||||
{
|
||||
string scriptLine = scriptFile.FileLines[this.Line - 1];
|
||||
return new FilePosition(this.scriptFile, this.Line, scriptLine.Length + 1);
|
||||
string scriptLine = scriptFile.FileLines[Line - 1];
|
||||
return new FilePosition(scriptFile, Line, scriptLine.Length + 1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -61,11 +61,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Join("\r\n", this.FileLines);
|
||||
return string.Join("\r\n", FileLines);
|
||||
}
|
||||
set
|
||||
{
|
||||
this.FileLines = value != null ? value.Split('\n') : null;
|
||||
FileLines = value != null ? value.Split('\n') : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,12 +118,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
string clientFilePath,
|
||||
TextReader textReader)
|
||||
{
|
||||
this.FilePath = filePath;
|
||||
this.ClientFilePath = clientFilePath;
|
||||
this.IsAnalysisEnabled = true;
|
||||
this.IsInMemory = Workspace.IsPathInMemory(filePath);
|
||||
FilePath = filePath;
|
||||
ClientFilePath = clientFilePath;
|
||||
IsAnalysisEnabled = true;
|
||||
IsInMemory = Workspace.IsPathInMemory(filePath);
|
||||
|
||||
this.SetFileContents(textReader.ReadToEnd());
|
||||
SetFileContents(textReader.ReadToEnd());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -137,11 +137,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
string clientFilePath,
|
||||
string initialBuffer)
|
||||
{
|
||||
this.FilePath = filePath;
|
||||
this.ClientFilePath = clientFilePath;
|
||||
this.IsAnalysisEnabled = true;
|
||||
FilePath = filePath;
|
||||
ClientFilePath = clientFilePath;
|
||||
IsAnalysisEnabled = true;
|
||||
|
||||
this.SetFileContents(initialBuffer);
|
||||
SetFileContents(initialBuffer);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -157,9 +157,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
{
|
||||
Validate.IsWithinRange(
|
||||
"lineNumber", lineNumber,
|
||||
1, this.FileLines.Count + 1);
|
||||
1, FileLines.Count + 1);
|
||||
|
||||
return this.FileLines[lineNumber - 1];
|
||||
return FileLines[lineNumber - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text under a specific range
|
||||
/// </summary>
|
||||
public string GetTextInRange(BufferRange range)
|
||||
{
|
||||
return string.Join(Environment.NewLine, GetLinesInRange(range));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -170,8 +178,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
/// <returns>An array of strings from the specified range of the file.</returns>
|
||||
public virtual string[] GetLinesInRange(BufferRange bufferRange)
|
||||
{
|
||||
this.ValidatePosition(bufferRange.Start);
|
||||
this.ValidatePosition(bufferRange.End);
|
||||
ValidatePosition(bufferRange.Start);
|
||||
ValidatePosition(bufferRange.End);
|
||||
|
||||
List<string> linesInRange = new List<string>();
|
||||
|
||||
@@ -180,7 +188,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
|
||||
for (int line = startLine; line <= endLine; line++)
|
||||
{
|
||||
string currentLine = this.FileLines[line - 1];
|
||||
string currentLine = FileLines[line - 1];
|
||||
int startColumn =
|
||||
line == startLine
|
||||
? bufferRange.Start.Column
|
||||
@@ -208,7 +216,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
/// <param name="bufferPosition">The position in the buffer to be validated.</param>
|
||||
public void ValidatePosition(BufferPosition bufferPosition)
|
||||
{
|
||||
this.ValidatePosition(
|
||||
ValidatePosition(
|
||||
bufferPosition.Line,
|
||||
bufferPosition.Column);
|
||||
}
|
||||
@@ -221,14 +229,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
/// <param name="column">The 1-based column to be validated.</param>
|
||||
public void ValidatePosition(int line, int column)
|
||||
{
|
||||
if (line < 1 || line > this.FileLines.Count + 1)
|
||||
if (line < 1 || line > FileLines.Count + 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(line), SR.WorkspaceServicePositionLineOutOfRange);
|
||||
}
|
||||
|
||||
// The maximum column is either one past the length of the string
|
||||
// or 1 if the string is empty.
|
||||
string lineString = this.FileLines[line - 1];
|
||||
string lineString = FileLines[line - 1];
|
||||
int maxColumn = lineString.Length > 0 ? lineString.Length + 1 : 1;
|
||||
|
||||
if (column < 1 || column > maxColumn)
|
||||
@@ -243,28 +251,28 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
/// <param name="fileChange">The FileChange to apply to the file's contents.</param>
|
||||
public void ApplyChange(FileChange fileChange)
|
||||
{
|
||||
this.ValidatePosition(fileChange.Line, fileChange.Offset);
|
||||
this.ValidatePosition(fileChange.EndLine, fileChange.EndOffset);
|
||||
ValidatePosition(fileChange.Line, fileChange.Offset);
|
||||
ValidatePosition(fileChange.EndLine, fileChange.EndOffset);
|
||||
|
||||
// Break up the change lines
|
||||
string[] changeLines = fileChange.InsertString.Split('\n');
|
||||
|
||||
// Get the first fragment of the first line
|
||||
string firstLineFragment =
|
||||
this.FileLines[fileChange.Line - 1]
|
||||
FileLines[fileChange.Line - 1]
|
||||
.Substring(0, fileChange.Offset - 1);
|
||||
|
||||
// Get the last fragment of the last line
|
||||
string endLine = this.FileLines[fileChange.EndLine - 1];
|
||||
string endLine = FileLines[fileChange.EndLine - 1];
|
||||
string lastLineFragment =
|
||||
endLine.Substring(
|
||||
fileChange.EndOffset - 1,
|
||||
(this.FileLines[fileChange.EndLine - 1].Length - fileChange.EndOffset) + 1);
|
||||
(FileLines[fileChange.EndLine - 1].Length - fileChange.EndOffset) + 1);
|
||||
|
||||
// Remove the old lines
|
||||
for (int i = 0; i <= fileChange.EndLine - fileChange.Line; i++)
|
||||
{
|
||||
this.FileLines.RemoveAt(fileChange.Line - 1);
|
||||
FileLines.RemoveAt(fileChange.Line - 1);
|
||||
}
|
||||
|
||||
// Build and insert the new lines
|
||||
@@ -287,7 +295,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
finalLine = finalLine + lastLineFragment;
|
||||
}
|
||||
|
||||
this.FileLines.Insert(currentLineNumber - 1, finalLine);
|
||||
FileLines.Insert(currentLineNumber - 1, finalLine);
|
||||
currentLineNumber++;
|
||||
}
|
||||
}
|
||||
@@ -301,7 +309,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
/// <returns>The zero-based offset for the given file position.</returns>
|
||||
public int GetOffsetAtPosition(int lineNumber, int columnNumber)
|
||||
{
|
||||
Validate.IsWithinRange("lineNumber", lineNumber, 1, this.FileLines.Count);
|
||||
Validate.IsWithinRange("lineNumber", lineNumber, 1, FileLines.Count);
|
||||
Validate.IsGreaterThan("columnNumber", columnNumber, 0);
|
||||
|
||||
int offset = 0;
|
||||
@@ -316,7 +324,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
else
|
||||
{
|
||||
// Add an offset to account for the current platform's newline characters
|
||||
offset += this.FileLines[i].Length + Environment.NewLine.Length;
|
||||
offset += FileLines[i].Length + Environment.NewLine.Length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,9 +347,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
int newLine = originalPosition.Line + lineOffset,
|
||||
newColumn = originalPosition.Column + columnOffset;
|
||||
|
||||
this.ValidatePosition(newLine, newColumn);
|
||||
ValidatePosition(newLine, newColumn);
|
||||
|
||||
string scriptLine = this.FileLines[newLine - 1];
|
||||
string scriptLine = FileLines[newLine - 1];
|
||||
newColumn = Math.Min(scriptLine.Length + 1, newColumn);
|
||||
|
||||
return new FilePosition(this, newLine, newColumn);
|
||||
@@ -379,9 +387,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
BufferPosition endPosition = startPosition;
|
||||
|
||||
int line = 0;
|
||||
while (line < this.FileLines.Count)
|
||||
while (line < FileLines.Count)
|
||||
{
|
||||
if (searchedOffset <= currentOffset + this.FileLines[line].Length)
|
||||
if (searchedOffset <= currentOffset + FileLines[line].Length)
|
||||
{
|
||||
int column = searchedOffset - currentOffset;
|
||||
|
||||
@@ -415,7 +423,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
}
|
||||
|
||||
// Increase the current offset and include newline length
|
||||
currentOffset += this.FileLines[line].Length + Environment.NewLine.Length;
|
||||
currentOffset += FileLines[line].Length + Environment.NewLine.Length;
|
||||
line++;
|
||||
}
|
||||
|
||||
@@ -430,7 +438,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
||||
{
|
||||
// Split the file contents into lines and trim
|
||||
// any carriage returns from the strings.
|
||||
this.FileLines =
|
||||
FileLines =
|
||||
fileContents
|
||||
.Split('\n')
|
||||
.Select(line => line.TrimEnd('\r'))
|
||||
|
||||
@@ -112,6 +112,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
|
||||
/// List of callbacks to call when a text document is closed
|
||||
/// </summary>
|
||||
private List<TextDocCloseCallback> TextDocCloseCallbacks { get; set; }
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -189,7 +190,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
|
||||
public void RegisterTextDocOpenCallback(TextDocOpenCallback task)
|
||||
{
|
||||
TextDocOpenCallbacks.Add(task);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -287,7 +288,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
|
||||
var configUpdateTasks = ConfigChangeCallbacks.Select(
|
||||
t => t(configChangeParams.Settings, CurrentSettings, eventContext));
|
||||
await Task.WhenAll(configUpdateTasks);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -28,8 +28,15 @@
|
||||
"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",
|
||||
"Microsoft.DiaSymReader.Native": "1.4.1"
|
||||
},
|
||||
"frameworks": {
|
||||
|
||||
@@ -113,18 +113,9 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.BatchParser
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyToOutput(string sourceDirectory, string filename)
|
||||
{
|
||||
File.Copy(Path.Combine(sourceDirectory, filename), filename, true);
|
||||
FileUtilities.SetFileReadWrite(filename);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchParserTest()
|
||||
{
|
||||
CopyToOutput(FilesLocation, "TS-err-cycle1.txt");
|
||||
CopyToOutput(FilesLocation, "cycle2.txt");
|
||||
|
||||
Start("err-blockComment");
|
||||
Start("err-blockComment2");
|
||||
Start("err-varDefinition");
|
||||
|
||||
@@ -135,5 +135,27 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
|
||||
SignatureHelp signatureHelp = service.GetSignatureHelp(textDocument, result.ScriptFile);
|
||||
Assert.NotNull(signatureHelp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test overwriting the binding queue context
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OverwriteBindingContext()
|
||||
{
|
||||
var result = TestObjects.InitLiveConnectionInfo();
|
||||
|
||||
// add a new connection context
|
||||
var connectionKey = LanguageService.Instance.BindingQueue.AddConnectionContext(result.ConnectionInfo, overwrite: true);
|
||||
Assert.True(LanguageService.Instance.BindingQueue.BindingContextMap.ContainsKey(connectionKey));
|
||||
|
||||
// cache the server connection
|
||||
var orgServerConnection = LanguageService.Instance.BindingQueue.BindingContextMap[connectionKey].ServerConnection;
|
||||
Assert.NotNull(orgServerConnection);
|
||||
|
||||
// add a new connection context
|
||||
connectionKey = LanguageService.Instance.BindingQueue.AddConnectionContext(result.ConnectionInfo, overwrite: true);
|
||||
Assert.True(LanguageService.Instance.BindingQueue.BindingContextMap.ContainsKey(connectionKey));
|
||||
Assert.False(object.ReferenceEquals(LanguageService.Instance.BindingQueue.BindingContextMap[connectionKey].ServerConnection, orgServerConnection));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
{
|
||||
internal class ComparisonFailureException : InvalidOperationException
|
||||
{
|
||||
internal string FullMessageWithDiff { get; private set; }
|
||||
internal string EditAndCopyMessage { get; private set; }
|
||||
|
||||
internal ComparisonFailureException(string fullMessageWithDiff, string editAndCopyMessage)
|
||||
: base(fullMessageWithDiff)
|
||||
{
|
||||
FullMessageWithDiff = fullMessageWithDiff;
|
||||
EditAndCopyMessage = editAndCopyMessage;
|
||||
}
|
||||
|
||||
internal ComparisonFailureException(string editAndCopyMessage)
|
||||
: base(editAndCopyMessage)
|
||||
{
|
||||
EditAndCopyMessage = FullMessageWithDiff = editAndCopyMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
@@ -5,10 +6,15 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains environment information needed when running tests.
|
||||
/// </summary>
|
||||
public class RunEnvironmentInfo
|
||||
{
|
||||
private static string cachedTestFolderPath;
|
||||
@@ -30,7 +36,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
public static string GetTestDataLocation()
|
||||
{
|
||||
string testFolderPath;
|
||||
string testPath = @"test\Microsoft.SqlTools.ServiceLayer.Test.Common\TestData";
|
||||
string testPath = Path.Combine("test", "Microsoft.SqlTools.ServiceLayer.Test.Common", "TestData");
|
||||
string projectPath = Environment.GetEnvironmentVariable(Constants.ProjectPath);
|
||||
|
||||
if (projectPath != null)
|
||||
@@ -45,13 +51,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
}
|
||||
else
|
||||
{
|
||||
string defaultPath = Path.Combine(typeof(Scripts).GetTypeInfo().Assembly.Location, @"..\..\..\..\..");
|
||||
testFolderPath = Path.Combine(defaultPath, @"Microsoft.SqlTools.ServiceLayer.Test.Common\TestData");
|
||||
// We are running tests locally, which means we expect to be running inside the bin\debug\netcoreapp directory
|
||||
// Test Files should be found at the root of the project so go back the necessary number of directories for this
|
||||
// to be found. We are manually specifying the testFolderPath here for clarity on where to expect this
|
||||
|
||||
string assemblyDir = Path.GetDirectoryName(typeof(Scripts).GetTypeInfo().Assembly.Location);
|
||||
string defaultPath = Path.Combine(assemblyDir, GoUpNDirectories(4));
|
||||
testFolderPath = Path.Combine(defaultPath, "Microsoft.SqlTools.ServiceLayer.Test.Common", "TestData");
|
||||
|
||||
cachedTestFolderPath = testFolderPath;
|
||||
}
|
||||
}
|
||||
return testFolderPath;
|
||||
}
|
||||
|
||||
private static string GoUpNDirectories(int n)
|
||||
{
|
||||
string up = ".." + (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "\\" : "/");
|
||||
return string.Concat(Enumerable.Repeat(up, n));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
return Enumerable.Empty<TestServerIdentity>();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception)
|
||||
{
|
||||
return Enumerable.Empty<TestServerIdentity>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
select *
|
||||
from mytable
|
||||
intersect
|
||||
select *
|
||||
from mytable
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
select *
|
||||
from mytable
|
||||
union
|
||||
select *
|
||||
from mytable
|
||||
union all
|
||||
select *
|
||||
from mytable
|
||||
except
|
||||
select *
|
||||
from mytable
|
||||
intersect
|
||||
select *
|
||||
from mytable
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
seLect *
|
||||
from mytable
|
||||
unIon
|
||||
selECT *
|
||||
fROM mytable
|
||||
union ALL
|
||||
select *
|
||||
from mytable
|
||||
excepT
|
||||
select *
|
||||
from mytable
|
||||
Intersect
|
||||
select *
|
||||
from mytable
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
SELECT *
|
||||
FROM mytable
|
||||
UNION
|
||||
SELECT *
|
||||
FROM mytable
|
||||
UNION ALL
|
||||
SELECT *
|
||||
FROM mytable
|
||||
EXCEPT
|
||||
SELECT *
|
||||
FROM mytable
|
||||
INTERSECT
|
||||
SELECT *
|
||||
FROM mytable
|
||||
@@ -0,0 +1,14 @@
|
||||
select *
|
||||
from mytable
|
||||
union
|
||||
select *
|
||||
from mytable
|
||||
except
|
||||
select *
|
||||
from mytable
|
||||
union all
|
||||
select *
|
||||
from mytable
|
||||
intersect
|
||||
select *
|
||||
from mytable
|
||||
@@ -0,0 +1,19 @@
|
||||
USE AdventureWorks2008R2;
|
||||
GO
|
||||
-- Define the CTE expression name and column list.
|
||||
WITH
|
||||
Sales_CTE (SalesPersonID, SalesOrderID, SalesYear)
|
||||
AS
|
||||
-- Define the CTE query.
|
||||
(
|
||||
SELECT SalesPersonID, SalesOrderID, YEAR(OrderDate) AS SalesYear
|
||||
FROM Sales.SalesOrderHeader
|
||||
WHERE SalesPersonID IS NOT NULL
|
||||
)
|
||||
-- Define the outer query referencing the CTE name.
|
||||
SELECT SalesPersonID, COUNT(SalesOrderID) AS TotalSales, SalesYear
|
||||
FROM Sales_CTE
|
||||
GROUP BY SalesYear, SalesPersonID
|
||||
ORDER BY SalesPersonID, SalesYear;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
USE AdventureWorks2008R2;
|
||||
GO
|
||||
-- Define the CTE expression name and column list.
|
||||
WITH
|
||||
Sales_CTE
|
||||
(
|
||||
SalesPersonID,
|
||||
SalesOrderID,
|
||||
SalesYear
|
||||
)
|
||||
AS
|
||||
-- Define the CTE query.
|
||||
(
|
||||
SELECT
|
||||
SalesPersonID,
|
||||
SalesOrderID,
|
||||
YEAR(OrderDate) AS SalesYear
|
||||
FROM
|
||||
Sales.SalesOrderHeader
|
||||
WHERE SalesPersonID IS NOT NULL
|
||||
)
|
||||
-- Define the outer query referencing the CTE name.
|
||||
SELECT
|
||||
SalesPersonID,
|
||||
COUNT(SalesOrderID) AS TotalSales,
|
||||
SalesYear
|
||||
FROM
|
||||
Sales_CTE
|
||||
GROUP BY SalesYear, SalesPersonID
|
||||
ORDER BY SalesPersonID, SalesYear;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
USE AdventureWorks2008R2;
|
||||
GO
|
||||
-- Define the CTE expression name and column list.
|
||||
WITH
|
||||
Sales_CTE (SalesPersonID ,SalesOrderID ,SalesYear)
|
||||
AS
|
||||
-- Define the CTE query.
|
||||
(
|
||||
SELECT SalesPersonID ,SalesOrderID ,YEAR(OrderDate) AS SalesYear
|
||||
FROM Sales.SalesOrderHeader
|
||||
WHERE SalesPersonID IS NOT NULL
|
||||
)
|
||||
-- Define the outer query referencing the CTE name.
|
||||
SELECT SalesPersonID ,COUNT(SalesOrderID) AS TotalSales ,SalesYear
|
||||
FROM Sales_CTE
|
||||
GROUP BY SalesYear, SalesPersonID
|
||||
ORDER BY SalesPersonID, SalesYear;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
USE AdventureWorks2008R2;
|
||||
GO
|
||||
-- Define the CTE expression name and column list.
|
||||
WITH
|
||||
Sales_CTE
|
||||
(
|
||||
SalesPersonID,
|
||||
SalesOrderID,
|
||||
SalesYear
|
||||
)
|
||||
AS
|
||||
-- Define the CTE query.
|
||||
(
|
||||
SELECT
|
||||
SalesPersonID,
|
||||
SalesOrderID,
|
||||
YEAR(OrderDate) AS SalesYear
|
||||
FROM
|
||||
Sales.SalesOrderHeader
|
||||
WHERE SalesPersonID IS NOT NULL
|
||||
)
|
||||
-- Define the outer query referencing the CTE name.
|
||||
SELECT
|
||||
SalesPersonID,
|
||||
COUNT(SalesOrderID) AS TotalSales,
|
||||
SalesYear
|
||||
FROM
|
||||
Sales_CTE
|
||||
GROUP BY SalesYear, SalesPersonID
|
||||
ORDER BY SalesPersonID, SalesYear;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
USE AdventureWorks2008R2;
|
||||
GO
|
||||
-- Define the CTE expression name and column list.
|
||||
WITH
|
||||
Sales_CTE
|
||||
(
|
||||
SalesPersonID
|
||||
,SalesOrderID
|
||||
,SalesYear
|
||||
)
|
||||
AS
|
||||
-- Define the CTE query.
|
||||
(
|
||||
SELECT
|
||||
SalesPersonID
|
||||
,SalesOrderID
|
||||
,YEAR(OrderDate) AS SalesYear
|
||||
FROM
|
||||
Sales.SalesOrderHeader
|
||||
WHERE SalesPersonID IS NOT NULL
|
||||
)
|
||||
-- Define the outer query referencing the CTE name.
|
||||
SELECT
|
||||
SalesPersonID
|
||||
,COUNT(SalesOrderID) AS TotalSales
|
||||
,SalesYear
|
||||
FROM
|
||||
Sales_CTE
|
||||
GROUP BY SalesYear, SalesPersonID
|
||||
ORDER BY SalesPersonID, SalesYear;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
use AdventureWorks2008R2;
|
||||
go
|
||||
-- Define the CTE expression name and column list.
|
||||
with
|
||||
Sales_CTE
|
||||
(
|
||||
SalesPersonID,
|
||||
SalesOrderID,
|
||||
SalesYear
|
||||
)
|
||||
as
|
||||
-- Define the CTE query.
|
||||
(
|
||||
select
|
||||
SalesPersonID,
|
||||
SalesOrderID,
|
||||
YEAR(OrderDate) as SalesYear
|
||||
from
|
||||
Sales.SalesOrderHeader
|
||||
where SalesPersonID is not null
|
||||
)
|
||||
-- Define the outer query referencing the CTE name.
|
||||
select
|
||||
SalesPersonID,
|
||||
COUNT(SalesOrderID) as TotalSales,
|
||||
SalesYear
|
||||
from
|
||||
Sales_CTE
|
||||
group by SalesYear, SalesPersonID
|
||||
order by SalesPersonID, SalesYear;
|
||||
go
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
with
|
||||
my_initial_table( column1, column2 )
|
||||
AS
|
||||
(
|
||||
select *
|
||||
from mytable
|
||||
),
|
||||
my_other_table( column1X, column2X )
|
||||
AS
|
||||
(
|
||||
select *
|
||||
from mytable2
|
||||
)
|
||||
select distinct top (10) PERCENT with ties
|
||||
alias1 = SIZE(mytable.mycol1), another = COUNT(new_elements), count(money) AS encore, id
|
||||
INTO my_new_table
|
||||
FROM my_initial_table
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user