mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -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
|
- dotnet test test/Microsoft.SqlTools.ServiceLayer.Test
|
||||||
|
|
||||||
env:
|
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.
32
build.cake
32
build.cake
@@ -1,4 +1,6 @@
|
|||||||
#addin "Newtonsoft.Json"
|
#addin "Newtonsoft.Json"
|
||||||
|
#addin "mssql.ResX"
|
||||||
|
#addin "mssql.XliffParser"
|
||||||
|
|
||||||
#load "scripts/runhelpers.cake"
|
#load "scripts/runhelpers.cake"
|
||||||
#load "scripts/archiving.cake"
|
#load "scripts/archiving.cake"
|
||||||
@@ -9,7 +11,8 @@ using System.ComponentModel;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Cake.Common.IO
|
using Cake.Common.IO;
|
||||||
|
using XliffParser;
|
||||||
|
|
||||||
// Basic arguments
|
// Basic arguments
|
||||||
var target = Argument("target", "Default");
|
var target = Argument("target", "Default");
|
||||||
@@ -511,10 +514,13 @@ Task("SRGen")
|
|||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
var projects = System.IO.Directory.GetFiles(sourceFolder, "project.json", SearchOption.AllDirectories).ToList();
|
var projects = System.IO.Directory.GetFiles(sourceFolder, "project.json", SearchOption.AllDirectories).ToList();
|
||||||
|
|
||||||
foreach(var project in projects) {
|
foreach(var project in projects) {
|
||||||
var projectDir = System.IO.Path.GetDirectoryName(project);
|
var projectDir = System.IO.Path.GetDirectoryName(project);
|
||||||
|
var localizationDir = System.IO.Path.Combine(projectDir, "Localization");
|
||||||
var projectName = (new System.IO.DirectoryInfo(projectDir)).Name;
|
var projectName = (new System.IO.DirectoryInfo(projectDir)).Name;
|
||||||
var projectStrings = System.IO.Path.Combine(projectDir, "sr.strings");
|
var projectNameSpace = projectName + ".Localization";
|
||||||
|
var projectStrings = System.IO.Path.Combine(localizationDir, "sr.strings");
|
||||||
|
|
||||||
if (!System.IO.File.Exists(projectStrings))
|
if (!System.IO.File.Exists(projectStrings))
|
||||||
{
|
{
|
||||||
@@ -523,8 +529,10 @@ Task("SRGen")
|
|||||||
}
|
}
|
||||||
|
|
||||||
var srgenPath = System.IO.Path.Combine(toolsFolder, "Microsoft.DataTools.SrGen", "lib", "netcoreapp1.0", "srgen.dll");
|
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 outputResx = System.IO.Path.Combine(localizationDir, "sr.resx");
|
||||||
var outputCs = System.IO.Path.Combine(projectDir, "sr.cs");
|
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");
|
||||||
|
|
||||||
// Delete preexisting resx and designer files
|
// Delete preexisting resx and designer files
|
||||||
if (System.IO.File.Exists(outputResx))
|
if (System.IO.File.Exists(outputResx))
|
||||||
@@ -538,10 +546,24 @@ Task("SRGen")
|
|||||||
|
|
||||||
// Run SRGen
|
// Run SRGen
|
||||||
var dotnetArgs = string.Format("{0} -or \"{1}\" -oc \"{2}\" -ns \"{3}\" -an \"{4}\" -cn SR -l CS -dnx \"{5}\"",
|
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);
|
srgenPath, outputResx, outputCs, projectName, projectNameSpace, projectStrings);
|
||||||
Information("{0}", dotnetArgs);
|
Information("{0}", dotnetArgs);
|
||||||
Run(dotnetcli, dotnetArgs)
|
Run(dotnetcli, dotnetArgs)
|
||||||
.ExceptionOnError("Failed to run SRGen.");
|
.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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ if(-Not $SkipToolPackageRestore.IsPresent)
|
|||||||
Set-Location $TOOLS_DIR
|
Set-Location $TOOLS_DIR
|
||||||
|
|
||||||
Write-Verbose -Message "Restoring tools from NuGet..."
|
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`""
|
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install $PACKAGES_CONFIG -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
|
||||||
Write-Verbose -Message ($NuGetOutput | out-string)
|
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ fi
|
|||||||
|
|
||||||
# Restore tools from NuGet.
|
# Restore tools from NuGet.
|
||||||
pushd "$TOOLS_DIR" >/dev/null
|
pushd "$TOOLS_DIR" >/dev/null
|
||||||
|
mono "$NUGET_EXE" config -configfile ../nuget.config
|
||||||
mono "$NUGET_EXE" install "$PACKAGES_CONFIG" -ExcludeVersion -OutputDirectory "$TOOLS_DIR"
|
mono "$NUGET_EXE" install "$PACKAGES_CONFIG" -ExcludeVersion -OutputDirectory "$TOOLS_DIR"
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "Could not restore NuGet packages."
|
echo "Could not restore NuGet packages."
|
||||||
|
|||||||
@@ -4,4 +4,5 @@
|
|||||||
<package id="Newtonsoft.Json" version="8.0.3" />
|
<package id="Newtonsoft.Json" version="8.0.3" />
|
||||||
<package id="Microsoft.DataTools.SrGen" version="1.0.2" />
|
<package id="Microsoft.DataTools.SrGen" version="1.0.2" />
|
||||||
<package id="Mono.TextTransform" version="1.0.0" />
|
<package id="Mono.TextTransform" version="1.0.0" />
|
||||||
|
<package id="mssql.XliffParser" version="0.5.3" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -328,7 +328,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
}
|
}
|
||||||
catch (ObjectDisposedException)
|
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
|
// open connection based on request details
|
||||||
result = await Instance.Connect(connectParams);
|
result = await Connect(connectParams);
|
||||||
await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -745,7 +748,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool result = Instance.CancelConnect(cancelParams);
|
bool result = CancelConnect(cancelParams);
|
||||||
await requestContext.SendResult(result);
|
await requestContext.SendResult(result);
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch(Exception ex)
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString);
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -63,7 +63,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (AmbientSettings.AlwaysRetryOnTransientFailure == true)
|
if (AmbientSettings.AlwaysRetryOnTransientFailure)
|
||||||
{
|
{
|
||||||
useRetry = true;
|
useRetry = true;
|
||||||
}
|
}
|
||||||
@@ -368,8 +368,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
|
|||||||
IDbCommand cmd = null;
|
IDbCommand cmd = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Debug.Assert(conn.State == ConnectionState.Open, "connection passed to ExecuteReader should be open.");
|
|
||||||
|
|
||||||
cmd = conn.CreateCommand();
|
cmd = conn.CreateCommand();
|
||||||
|
|
||||||
if (initializeCommand == null)
|
if (initializeCommand == null)
|
||||||
@@ -603,11 +601,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
|
|||||||
Validate.IsNotNullOrEmptyString(nameof(commandText), commandText);
|
Validate.IsNotNullOrEmptyString(nameof(commandText), commandText);
|
||||||
|
|
||||||
string filePath = ExecuteScalar(conn, commandText, initializeCommand, catchException) as string;
|
string filePath = ExecuteScalar(conn, commandText, initializeCommand, catchException) as string;
|
||||||
if (!String.IsNullOrWhiteSpace(filePath))
|
if (!string.IsNullOrWhiteSpace(filePath))
|
||||||
{
|
{
|
||||||
// Remove filename from the filePath
|
// Remove filename from the filePath
|
||||||
Uri pathUri;
|
Uri pathUri;
|
||||||
if (Uri.TryCreate(filePath, UriKind.Absolute, out pathUri) == false)
|
if (!Uri.TryCreate(filePath, UriKind.Absolute, out pathUri))
|
||||||
{
|
{
|
||||||
// Invalid Uri
|
// Invalid Uri
|
||||||
return null;
|
return null;
|
||||||
@@ -931,7 +929,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
|
|||||||
|
|
||||||
serverInfo = GetServerVersion(connection);
|
serverInfo = GetServerVersion(connection);
|
||||||
|
|
||||||
tempDatabaseName = (String.IsNullOrEmpty(builder.InitialCatalog) == false) ?
|
tempDatabaseName = (!string.IsNullOrEmpty(builder.InitialCatalog)) ?
|
||||||
builder.InitialCatalog : builder.AttachDBFilename;
|
builder.InitialCatalog : builder.AttachDBFilename;
|
||||||
|
|
||||||
// If at this point the dbName remained an empty string then
|
// If at this point the dbName remained an empty string then
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Credentials
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal CredentialService(ICredentialStore store, StoreConfig config)
|
internal CredentialService(ICredentialStore store, StoreConfig config)
|
||||||
{
|
{
|
||||||
this.credStore = store != null ? store : GetStoreForOS(config);
|
credStore = store != null ? store : GetStoreForOS(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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? DocumentHighlightProvider { get; set; }
|
||||||
|
|
||||||
|
public bool? DocumentFormattingProvider { get; set; }
|
||||||
|
|
||||||
|
public bool? DocumentRangeFormattingProvider { get; set; }
|
||||||
|
|
||||||
public bool? DocumentSymbolProvider { get; set; }
|
public bool? DocumentSymbolProvider { get; set; }
|
||||||
|
|
||||||
public bool? WorkspaceSymbolProvider { 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>
|
/// </summary>
|
||||||
public class ProtocolEndpoint : IProtocolEndpoint
|
public class ProtocolEndpoint : IProtocolEndpoint
|
||||||
{
|
{
|
||||||
|
private bool isInitialized;
|
||||||
private bool isStarted;
|
private bool isStarted;
|
||||||
private int currentMessageId;
|
private int currentMessageId;
|
||||||
private ChannelBase protocolChannel;
|
private ChannelBase protocolChannel;
|
||||||
@@ -64,12 +65,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts the language server client and sends the Initialize method.
|
/// Initializes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A Task that can be awaited for initialization to complete.</returns>
|
public void Initialize()
|
||||||
public async Task Start()
|
|
||||||
{
|
{
|
||||||
if (!this.isStarted)
|
if (!this.isInitialized)
|
||||||
{
|
{
|
||||||
// Start the provided protocol channel
|
// Start the provided protocol channel
|
||||||
this.protocolChannel.Start(this.messageProtocolType);
|
this.protocolChannel.Start(this.messageProtocolType);
|
||||||
@@ -83,6 +83,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
|||||||
// Listen for unhandled exceptions from the dispatcher
|
// Listen for unhandled exceptions from the dispatcher
|
||||||
this.MessageDispatcher.UnhandledException += MessageDispatcher_UnhandledException;
|
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
|
// Notify implementation about endpoint start
|
||||||
await this.OnStart();
|
await this.OnStart();
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provide initialization that must occur after the service host is started
|
/// Provide initialization that must occur after the service host is started
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Initialize()
|
public void InitializeRequestHandlers()
|
||||||
{
|
{
|
||||||
// Register the requests that this service host will handle
|
// Register the requests that this service host will handle
|
||||||
this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
|
this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
|
||||||
@@ -153,6 +153,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
|
|||||||
TextDocumentSync = TextDocumentSyncKind.Incremental,
|
TextDocumentSync = TextDocumentSyncKind.Incremental,
|
||||||
DefinitionProvider = true,
|
DefinitionProvider = true,
|
||||||
ReferencesProvider = false,
|
ReferencesProvider = false,
|
||||||
|
DocumentFormattingProvider = true,
|
||||||
|
DocumentRangeFormattingProvider = true,
|
||||||
DocumentHighlightProvider = false,
|
DocumentHighlightProvider = false,
|
||||||
HoverProvider = true,
|
HoverProvider = true,
|
||||||
CompletionProvider = new CompletionOptions
|
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
|
private bool HasPendingQueueItems
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|||||||
@@ -51,8 +51,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use a ConnectionInfo item to create a connected binding context
|
/// Use a ConnectionInfo item to create a connected binding context
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="connInfo"></param>
|
/// <param name="connInfo">Connection info used to create binding context</param>
|
||||||
public virtual string AddConnectionContext(ConnectionInfo connInfo)
|
/// <param name="overwrite">Overwrite existing context</param>
|
||||||
|
public virtual string AddConnectionContext(ConnectionInfo connInfo, bool overwrite = false)
|
||||||
{
|
{
|
||||||
if (connInfo == null)
|
if (connInfo == null)
|
||||||
{
|
{
|
||||||
@@ -62,10 +63,17 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
// lookup the current binding context
|
// lookup the current binding context
|
||||||
string connectionKey = GetConnectionContextKey(connInfo);
|
string connectionKey = GetConnectionContextKey(connInfo);
|
||||||
if (BindingContextExists(connectionKey))
|
if (BindingContextExists(connectionKey))
|
||||||
|
{
|
||||||
|
if (overwrite)
|
||||||
|
{
|
||||||
|
RemoveBindingContext(connectionKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// no need to populate the context again since the context already exists
|
// no need to populate the context again since the context already exists
|
||||||
return connectionKey;
|
return connectionKey;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey);
|
IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey);
|
||||||
|
|
||||||
if (bindingContext.BindingLock.WaitOne())
|
if (bindingContext.BindingLock.WaitOne())
|
||||||
|
|||||||
@@ -45,14 +45,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
|||||||
Reference = 18
|
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}")]
|
[DebuggerDisplay("Kind = {Kind.ToString()}, Label = {Label}, Detail = {Detail}")]
|
||||||
public class CompletionItem
|
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(HoverRequest.Type, HandleHoverRequest);
|
||||||
serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
|
serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
|
||||||
serviceHost.SetRequestHandler(DefinitionRequest.Type, HandleDefinitionRequest);
|
serviceHost.SetRequestHandler(DefinitionRequest.Type, HandleDefinitionRequest);
|
||||||
|
serviceHost.SetEventHandler(RebuildIntelliSenseNotification.Type, HandleRebuildIntelliSenseNotification);
|
||||||
|
|
||||||
// Register a no-op shutdown task for validation of the shutdown logic
|
// Register a no-op shutdown task for validation of the shutdown logic
|
||||||
serviceHost.RegisterShutdownTask(async (shutdownParams, shutdownRequestContext) =>
|
serviceHost.RegisterShutdownTask(async (shutdownParams, shutdownRequestContext) =>
|
||||||
@@ -442,6 +443,62 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
await Task.FromResult(true);
|
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>
|
/// <summary>
|
||||||
/// Handle the file configuration change notification
|
/// Handle the file configuration change notification
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// </auto-generated>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer {
|
namespace Microsoft.SqlTools.ServiceLayer.Localization {
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ namespace Microsoft.SqlTools.ServiceLayer {
|
|||||||
public static global::System.Resources.ResourceManager ResourceManager {
|
public static global::System.Resources.ResourceManager ResourceManager {
|
||||||
get {
|
get {
|
||||||
if (object.ReferenceEquals(resourceMan, null)) {
|
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;
|
resourceMan = temp;
|
||||||
}
|
}
|
||||||
return resourceMan;
|
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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Message header must separate key and value using ':'.
|
/// Looks up a localized string similar to Message header must separate key and value using ':'.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to This feature is currently not supported on Azure SQL DB and Data Warehouse: {0}.
|
/// Looks up a localized string similar to This feature is currently not supported on Azure SQL DB and Data Warehouse: {0}.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to EN_LOCALIZATION.
|
/// Looks up a localized string similar to EN_LOCALIZATION.
|
||||||
/// </summary>
|
/// </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
|
public static string HostingUnexpectedEndOfStream
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -413,6 +469,14 @@ namespace Microsoft.SqlTools.ServiceLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ErrorEmptyStringReplacement
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Keys.GetString(Keys.ErrorEmptyStringReplacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string WorkspaceServicePositionLineOutOfRange
|
public static string WorkspaceServicePositionLineOutOfRange
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -724,7 +788,7 @@ namespace Microsoft.SqlTools.ServiceLayer
|
|||||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
public class Keys
|
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;
|
static CultureInfo _culture = null;
|
||||||
|
|
||||||
@@ -774,6 +838,27 @@ namespace Microsoft.SqlTools.ServiceLayer
|
|||||||
public const string CredentialServiceWin32CredentialDisposed = "CredentialServiceWin32CredentialDisposed";
|
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";
|
public const string HostingUnexpectedEndOfStream = "HostingUnexpectedEndOfStream";
|
||||||
|
|
||||||
|
|
||||||
@@ -903,6 +988,9 @@ namespace Microsoft.SqlTools.ServiceLayer
|
|||||||
public const string PeekDefinitionTypeNotSupportedError = "PeekDefinitionTypeNotSupportedError";
|
public const string PeekDefinitionTypeNotSupportedError = "PeekDefinitionTypeNotSupportedError";
|
||||||
|
|
||||||
|
|
||||||
|
public const string ErrorEmptyStringReplacement = "ErrorEmptyStringReplacement";
|
||||||
|
|
||||||
|
|
||||||
public const string WorkspaceServicePositionLineOutOfRange = "WorkspaceServicePositionLineOutOfRange";
|
public const string WorkspaceServicePositionLineOutOfRange = "WorkspaceServicePositionLineOutOfRange";
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@@ -181,6 +181,34 @@
|
|||||||
<value>Win32Credential object is already disposed</value>
|
<value>Win32Credential object is already disposed</value>
|
||||||
<comment></comment>
|
<comment></comment>
|
||||||
</data>
|
</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">
|
<data name="HostingUnexpectedEndOfStream" xml:space="preserve">
|
||||||
<value>MessageReader's input stream ended unexpectedly, terminating</value>
|
<value>MessageReader's input stream ended unexpectedly, terminating</value>
|
||||||
<comment></comment>
|
<comment></comment>
|
||||||
@@ -359,6 +387,10 @@
|
|||||||
<value>This object type is currently not supported by this feature.</value>
|
<value>This object type is currently not supported by this feature.</value>
|
||||||
<comment></comment>
|
<comment></comment>
|
||||||
</data>
|
</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">
|
<data name="WorkspaceServicePositionLineOutOfRange" xml:space="preserve">
|
||||||
<value>Position is outside of file line range</value>
|
<value>Position is outside of file line range</value>
|
||||||
<comment></comment>
|
<comment></comment>
|
||||||
@@ -59,6 +59,26 @@ CredentialsServiceTargetForLookup = Target must be specified to check existance
|
|||||||
|
|
||||||
CredentialServiceWin32CredentialDisposed = Win32Credential object is already disposed
|
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
|
# Hosting
|
||||||
|
|
||||||
@@ -168,6 +188,8 @@ PeekDefinitionTimedoutError = Operation timed out.
|
|||||||
|
|
||||||
PeekDefinitionTypeNotSupportedError = This object type is currently not supported by this feature.
|
PeekDefinitionTypeNotSupportedError = This object type is currently not supported by this feature.
|
||||||
|
|
||||||
|
ErrorEmptyStringReplacement = Replacement of an empty string by an empty string.
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
# Workspace Service
|
# 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 System;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
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;
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer
|
namespace Microsoft.SqlTools.ServiceLayer
|
||||||
@@ -39,22 +34,10 @@ namespace Microsoft.SqlTools.ServiceLayer
|
|||||||
var hostDetails = new HostDetails(version: new Version(1, 0));
|
var hostDetails = new HostDetails(version: new Version(1, 0));
|
||||||
|
|
||||||
SqlToolsContext sqlToolsContext = new SqlToolsContext(hostDetails);
|
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();
|
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>
|
/// </summary>
|
||||||
public SqlToolsSettingsValues()
|
public SqlToolsSettingsValues()
|
||||||
{
|
{
|
||||||
|
IntelliSense = new IntelliSenseSettings();
|
||||||
this.IntelliSense = new IntelliSenseSettings();
|
QueryExecutionSettings = new QueryExecutionSettings();
|
||||||
this.QueryExecutionSettings = new QueryExecutionSettings();
|
Format = new FormatterSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -127,5 +127,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlContext
|
|||||||
/// Gets or sets the query execution settings
|
/// Gets or sets the query execution settings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QueryExecutionSettings QueryExecutionSettings { get; set; }
|
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>
|
/// <returns>A new FilePosition instance for the calculated position.</returns>
|
||||||
public FilePosition AddOffset(int lineOffset, int columnOffset)
|
public FilePosition AddOffset(int lineOffset, int columnOffset)
|
||||||
{
|
{
|
||||||
return this.scriptFile.CalculatePosition(
|
return scriptFile.CalculatePosition(
|
||||||
this,
|
this,
|
||||||
lineOffset,
|
lineOffset,
|
||||||
columnOffset);
|
columnOffset);
|
||||||
@@ -77,7 +77,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
/// <returns>A new FilePosition instance for the calculated position.</returns>
|
/// <returns>A new FilePosition instance for the calculated position.</returns>
|
||||||
public FilePosition GetLineStart()
|
public FilePosition GetLineStart()
|
||||||
{
|
{
|
||||||
string scriptLine = scriptFile.FileLines[this.Line - 1];
|
string scriptLine = scriptFile.FileLines[Line - 1];
|
||||||
|
|
||||||
int lineStartColumn = 1;
|
int lineStartColumn = 1;
|
||||||
for (int i = 0; i < scriptLine.Length; i++)
|
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>
|
/// <summary>
|
||||||
@@ -99,8 +99,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
/// <returns>A new FilePosition instance for the calculated position.</returns>
|
/// <returns>A new FilePosition instance for the calculated position.</returns>
|
||||||
public FilePosition GetLineEnd()
|
public FilePosition GetLineEnd()
|
||||||
{
|
{
|
||||||
string scriptLine = scriptFile.FileLines[this.Line - 1];
|
string scriptLine = scriptFile.FileLines[Line - 1];
|
||||||
return new FilePosition(this.scriptFile, this.Line, scriptLine.Length + 1);
|
return new FilePosition(scriptFile, Line, scriptLine.Length + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -61,11 +61,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return string.Join("\r\n", this.FileLines);
|
return string.Join("\r\n", FileLines);
|
||||||
}
|
}
|
||||||
set
|
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,
|
string clientFilePath,
|
||||||
TextReader textReader)
|
TextReader textReader)
|
||||||
{
|
{
|
||||||
this.FilePath = filePath;
|
FilePath = filePath;
|
||||||
this.ClientFilePath = clientFilePath;
|
ClientFilePath = clientFilePath;
|
||||||
this.IsAnalysisEnabled = true;
|
IsAnalysisEnabled = true;
|
||||||
this.IsInMemory = Workspace.IsPathInMemory(filePath);
|
IsInMemory = Workspace.IsPathInMemory(filePath);
|
||||||
|
|
||||||
this.SetFileContents(textReader.ReadToEnd());
|
SetFileContents(textReader.ReadToEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -137,11 +137,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
string clientFilePath,
|
string clientFilePath,
|
||||||
string initialBuffer)
|
string initialBuffer)
|
||||||
{
|
{
|
||||||
this.FilePath = filePath;
|
FilePath = filePath;
|
||||||
this.ClientFilePath = clientFilePath;
|
ClientFilePath = clientFilePath;
|
||||||
this.IsAnalysisEnabled = true;
|
IsAnalysisEnabled = true;
|
||||||
|
|
||||||
this.SetFileContents(initialBuffer);
|
SetFileContents(initialBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -157,9 +157,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
{
|
{
|
||||||
Validate.IsWithinRange(
|
Validate.IsWithinRange(
|
||||||
"lineNumber", lineNumber,
|
"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>
|
/// <summary>
|
||||||
@@ -170,8 +178,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
/// <returns>An array of strings from the specified range of the file.</returns>
|
/// <returns>An array of strings from the specified range of the file.</returns>
|
||||||
public virtual string[] GetLinesInRange(BufferRange bufferRange)
|
public virtual string[] GetLinesInRange(BufferRange bufferRange)
|
||||||
{
|
{
|
||||||
this.ValidatePosition(bufferRange.Start);
|
ValidatePosition(bufferRange.Start);
|
||||||
this.ValidatePosition(bufferRange.End);
|
ValidatePosition(bufferRange.End);
|
||||||
|
|
||||||
List<string> linesInRange = new List<string>();
|
List<string> linesInRange = new List<string>();
|
||||||
|
|
||||||
@@ -180,7 +188,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
|
|
||||||
for (int line = startLine; line <= endLine; line++)
|
for (int line = startLine; line <= endLine; line++)
|
||||||
{
|
{
|
||||||
string currentLine = this.FileLines[line - 1];
|
string currentLine = FileLines[line - 1];
|
||||||
int startColumn =
|
int startColumn =
|
||||||
line == startLine
|
line == startLine
|
||||||
? bufferRange.Start.Column
|
? 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>
|
/// <param name="bufferPosition">The position in the buffer to be validated.</param>
|
||||||
public void ValidatePosition(BufferPosition bufferPosition)
|
public void ValidatePosition(BufferPosition bufferPosition)
|
||||||
{
|
{
|
||||||
this.ValidatePosition(
|
ValidatePosition(
|
||||||
bufferPosition.Line,
|
bufferPosition.Line,
|
||||||
bufferPosition.Column);
|
bufferPosition.Column);
|
||||||
}
|
}
|
||||||
@@ -221,14 +229,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
/// <param name="column">The 1-based column to be validated.</param>
|
/// <param name="column">The 1-based column to be validated.</param>
|
||||||
public void ValidatePosition(int line, int column)
|
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);
|
throw new ArgumentOutOfRangeException(nameof(line), SR.WorkspaceServicePositionLineOutOfRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The maximum column is either one past the length of the string
|
// The maximum column is either one past the length of the string
|
||||||
// or 1 if the string is empty.
|
// 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;
|
int maxColumn = lineString.Length > 0 ? lineString.Length + 1 : 1;
|
||||||
|
|
||||||
if (column < 1 || column > maxColumn)
|
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>
|
/// <param name="fileChange">The FileChange to apply to the file's contents.</param>
|
||||||
public void ApplyChange(FileChange fileChange)
|
public void ApplyChange(FileChange fileChange)
|
||||||
{
|
{
|
||||||
this.ValidatePosition(fileChange.Line, fileChange.Offset);
|
ValidatePosition(fileChange.Line, fileChange.Offset);
|
||||||
this.ValidatePosition(fileChange.EndLine, fileChange.EndOffset);
|
ValidatePosition(fileChange.EndLine, fileChange.EndOffset);
|
||||||
|
|
||||||
// Break up the change lines
|
// Break up the change lines
|
||||||
string[] changeLines = fileChange.InsertString.Split('\n');
|
string[] changeLines = fileChange.InsertString.Split('\n');
|
||||||
|
|
||||||
// Get the first fragment of the first line
|
// Get the first fragment of the first line
|
||||||
string firstLineFragment =
|
string firstLineFragment =
|
||||||
this.FileLines[fileChange.Line - 1]
|
FileLines[fileChange.Line - 1]
|
||||||
.Substring(0, fileChange.Offset - 1);
|
.Substring(0, fileChange.Offset - 1);
|
||||||
|
|
||||||
// Get the last fragment of the last line
|
// Get the last fragment of the last line
|
||||||
string endLine = this.FileLines[fileChange.EndLine - 1];
|
string endLine = FileLines[fileChange.EndLine - 1];
|
||||||
string lastLineFragment =
|
string lastLineFragment =
|
||||||
endLine.Substring(
|
endLine.Substring(
|
||||||
fileChange.EndOffset - 1,
|
fileChange.EndOffset - 1,
|
||||||
(this.FileLines[fileChange.EndLine - 1].Length - fileChange.EndOffset) + 1);
|
(FileLines[fileChange.EndLine - 1].Length - fileChange.EndOffset) + 1);
|
||||||
|
|
||||||
// Remove the old lines
|
// Remove the old lines
|
||||||
for (int i = 0; i <= fileChange.EndLine - fileChange.Line; i++)
|
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
|
// Build and insert the new lines
|
||||||
@@ -287,7 +295,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
finalLine = finalLine + lastLineFragment;
|
finalLine = finalLine + lastLineFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.FileLines.Insert(currentLineNumber - 1, finalLine);
|
FileLines.Insert(currentLineNumber - 1, finalLine);
|
||||||
currentLineNumber++;
|
currentLineNumber++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,7 +309,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
/// <returns>The zero-based offset for the given file position.</returns>
|
/// <returns>The zero-based offset for the given file position.</returns>
|
||||||
public int GetOffsetAtPosition(int lineNumber, int columnNumber)
|
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);
|
Validate.IsGreaterThan("columnNumber", columnNumber, 0);
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
@@ -316,7 +324,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Add an offset to account for the current platform's newline characters
|
// 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,
|
int newLine = originalPosition.Line + lineOffset,
|
||||||
newColumn = originalPosition.Column + columnOffset;
|
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);
|
newColumn = Math.Min(scriptLine.Length + 1, newColumn);
|
||||||
|
|
||||||
return new FilePosition(this, newLine, newColumn);
|
return new FilePosition(this, newLine, newColumn);
|
||||||
@@ -379,9 +387,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
BufferPosition endPosition = startPosition;
|
BufferPosition endPosition = startPosition;
|
||||||
|
|
||||||
int line = 0;
|
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;
|
int column = searchedOffset - currentOffset;
|
||||||
|
|
||||||
@@ -415,7 +423,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Increase the current offset and include newline length
|
// Increase the current offset and include newline length
|
||||||
currentOffset += this.FileLines[line].Length + Environment.NewLine.Length;
|
currentOffset += FileLines[line].Length + Environment.NewLine.Length;
|
||||||
line++;
|
line++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +438,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
|
|||||||
{
|
{
|
||||||
// Split the file contents into lines and trim
|
// Split the file contents into lines and trim
|
||||||
// any carriage returns from the strings.
|
// any carriage returns from the strings.
|
||||||
this.FileLines =
|
FileLines =
|
||||||
fileContents
|
fileContents
|
||||||
.Split('\n')
|
.Split('\n')
|
||||||
.Select(line => line.TrimEnd('\r'))
|
.Select(line => line.TrimEnd('\r'))
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private List<TextDocCloseCallback> TextDocCloseCallbacks { get; set; }
|
private List<TextDocCloseCallback> TextDocCloseCallbacks { get; set; }
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Methods
|
#region Public Methods
|
||||||
|
|||||||
@@ -28,8 +28,15 @@
|
|||||||
"NETStandard.Library": "1.6.0",
|
"NETStandard.Library": "1.6.0",
|
||||||
"Microsoft.NETCore.Runtime.CoreCLR": "1.0.2",
|
"Microsoft.NETCore.Runtime.CoreCLR": "1.0.2",
|
||||||
"Microsoft.NETCore.DotNetHostPolicy": "1.0.1",
|
"Microsoft.NETCore.DotNetHostPolicy": "1.0.1",
|
||||||
|
"Microsoft.DiaSymReader.Native": "1.4.1",
|
||||||
"System.Diagnostics.Process": "4.1.0",
|
"System.Diagnostics.Process": "4.1.0",
|
||||||
"System.Threading.Thread": "4.0.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"
|
"Microsoft.DiaSymReader.Native": "1.4.1"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"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]
|
[Fact]
|
||||||
public void BatchParserTest()
|
public void BatchParserTest()
|
||||||
{
|
{
|
||||||
CopyToOutput(FilesLocation, "TS-err-cycle1.txt");
|
|
||||||
CopyToOutput(FilesLocation, "cycle2.txt");
|
|
||||||
|
|
||||||
Start("err-blockComment");
|
Start("err-blockComment");
|
||||||
Start("err-blockComment2");
|
Start("err-blockComment2");
|
||||||
Start("err-varDefinition");
|
Start("err-varDefinition");
|
||||||
|
|||||||
@@ -135,5 +135,27 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
|
|||||||
SignatureHelp signatureHelp = service.GetSignatureHelp(textDocument, result.ScriptFile);
|
SignatureHelp signatureHelp = service.GetSignatureHelp(textDocument, result.ScriptFile);
|
||||||
Assert.NotNull(signatureHelp);
|
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.
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
@@ -5,10 +6,15 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains environment information needed when running tests.
|
||||||
|
/// </summary>
|
||||||
public class RunEnvironmentInfo
|
public class RunEnvironmentInfo
|
||||||
{
|
{
|
||||||
private static string cachedTestFolderPath;
|
private static string cachedTestFolderPath;
|
||||||
@@ -30,7 +36,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
|||||||
public static string GetTestDataLocation()
|
public static string GetTestDataLocation()
|
||||||
{
|
{
|
||||||
string testFolderPath;
|
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);
|
string projectPath = Environment.GetEnvironmentVariable(Constants.ProjectPath);
|
||||||
|
|
||||||
if (projectPath != null)
|
if (projectPath != null)
|
||||||
@@ -45,13 +51,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string defaultPath = Path.Combine(typeof(Scripts).GetTypeInfo().Assembly.Location, @"..\..\..\..\..");
|
// We are running tests locally, which means we expect to be running inside the bin\debug\netcoreapp directory
|
||||||
testFolderPath = Path.Combine(defaultPath, @"Microsoft.SqlTools.ServiceLayer.Test.Common\TestData");
|
// 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;
|
cachedTestFolderPath = testFolderPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 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>();
|
return Enumerable.Empty<TestServerIdentity>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<TestServerIdentity>();
|
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