Merge branch 'dev' of github.com:Microsoft/sqltoolsservice into dev

This commit is contained in:
Leila Lali
2017-02-17 09:53:17 -08:00
242 changed files with 8906 additions and 464 deletions

View File

@@ -39,5 +39,6 @@ script:
- dotnet test test/Microsoft.SqlTools.ServiceLayer.Test
env:
- ProjectPath=home/travis/build/Microsoft/sqltoolsservice
# Since we are building from root, current directory is the project path
- ProjectPath=./

BIN
bin/nuget/mssql.ResX.nupkg Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,6 @@
#addin "Newtonsoft.Json"
#addin "mssql.ResX"
#addin "mssql.XliffParser"
#load "scripts/runhelpers.cake"
#load "scripts/archiving.cake"
@@ -9,7 +11,8 @@ using System.ComponentModel;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Cake.Common.IO
using Cake.Common.IO;
using XliffParser;
// Basic arguments
var target = Argument("target", "Default");
@@ -510,39 +513,58 @@ Task("SetPackageVersions")
Task("SRGen")
.Does(() =>
{
var projects = System.IO.Directory.GetFiles(sourceFolder, "project.json", SearchOption.AllDirectories).ToList();
foreach(var project in projects) {
var projectDir = System.IO.Path.GetDirectoryName(project);
var projectName = (new System.IO.DirectoryInfo(projectDir)).Name;
var projectStrings = System.IO.Path.Combine(projectDir, "sr.strings");
var projects = System.IO.Directory.GetFiles(sourceFolder, "project.json", SearchOption.AllDirectories).ToList();
if (!System.IO.File.Exists(projectStrings))
{
Information("Project {0} doesn't contain 'sr.strings' file", projectName);
continue;
}
foreach(var project in projects) {
var projectDir = System.IO.Path.GetDirectoryName(project);
var localizationDir = System.IO.Path.Combine(projectDir, "Localization");
var projectName = (new System.IO.DirectoryInfo(projectDir)).Name;
var projectNameSpace = projectName + ".Localization";
var projectStrings = System.IO.Path.Combine(localizationDir, "sr.strings");
var srgenPath = System.IO.Path.Combine(toolsFolder, "Microsoft.DataTools.SrGen", "lib", "netcoreapp1.0", "srgen.dll");
var outputResx = System.IO.Path.Combine(projectDir, "sr.resx");
var outputCs = System.IO.Path.Combine(projectDir, "sr.cs");
if (!System.IO.File.Exists(projectStrings))
{
Information("Project {0} doesn't contain 'sr.strings' file", projectName);
continue;
}
// Delete preexisting resx and designer files
if (System.IO.File.Exists(outputResx))
{
System.IO.File.Delete(outputResx);
}
if (System.IO.File.Exists(outputCs))
{
System.IO.File.Delete(outputCs);
}
var srgenPath = System.IO.Path.Combine(toolsFolder, "Microsoft.DataTools.SrGen", "lib", "netcoreapp1.0", "srgen.dll");
var outputResx = System.IO.Path.Combine(localizationDir, "sr.resx");
var inputXliff = System.IO.Path.Combine(localizationDir, "transXliff");
var outputXlf = System.IO.Path.Combine(localizationDir, "sr.xlf");
var outputCs = System.IO.Path.Combine(localizationDir, "sr.cs");
// Run SRGen
var dotnetArgs = string.Format("{0} -or \"{1}\" -oc \"{2}\" -ns \"{3}\" -an \"{4}\" -cn SR -l CS -dnx \"{5}\"",
srgenPath, outputResx, outputCs, projectName, projectName, projectStrings);
Information("{0}", dotnetArgs);
Run(dotnetcli, dotnetArgs)
.ExceptionOnError("Failed to run SRGen.");
}
// Delete preexisting resx and designer files
if (System.IO.File.Exists(outputResx))
{
System.IO.File.Delete(outputResx);
}
if (System.IO.File.Exists(outputCs))
{
System.IO.File.Delete(outputCs);
}
// Run SRGen
var dotnetArgs = string.Format("{0} -or \"{1}\" -oc \"{2}\" -ns \"{3}\" -an \"{4}\" -cn SR -l CS -dnx \"{5}\"",
srgenPath, outputResx, outputCs, projectName, projectNameSpace, projectStrings);
Information("{0}", dotnetArgs);
Run(dotnetcli, dotnetArgs)
.ExceptionOnError("Failed to run SRGen.");
// Update XLF file from new Resx file
var doc = new XliffParser.XlfDocument(outputXlf);
doc.UpdateFromSource();
doc.Save();
// Update ResX files from new xliff files
var xlfDocNames = System.IO.Directory.GetFiles(inputXliff, "*.xlf", SearchOption.AllDirectories).ToList();
foreach(var docName in xlfDocNames)
{
var xlfDoc = new XliffParser.XlfDocument(docName);
var newPath = System.IO.Path.Combine(localizationDir, System.IO.Path.GetFileName(docName));
xlfDoc.SaveAsResX(newPath.Replace("xlf","resx"));
}
}
});
/// <summary>

View File

@@ -89,6 +89,7 @@ if(-Not $SkipToolPackageRestore.IsPresent)
Set-Location $TOOLS_DIR
Write-Verbose -Message "Restoring tools from NuGet..."
$NuGetConfig = Invoke-Expression "&`"$NUGET_EXE`" config -configfile ../nuget.config"
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install $PACKAGES_CONFIG -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
Write-Verbose -Message ($NuGetOutput | out-string)

View File

@@ -48,6 +48,7 @@ fi
# Restore tools from NuGet.
pushd "$TOOLS_DIR" >/dev/null
mono "$NUGET_EXE" config -configfile ../nuget.config
mono "$NUGET_EXE" install "$PACKAGES_CONFIG" -ExcludeVersion -OutputDirectory "$TOOLS_DIR"
if [ $? -ne 0 ]; then
echo "Could not restore NuGet packages."

View File

@@ -4,4 +4,5 @@
<package id="Newtonsoft.Json" version="8.0.3" />
<package id="Microsoft.DataTools.SrGen" version="1.0.2" />
<package id="Mono.TextTransform" version="1.0.0" />
<package id="mssql.XliffParser" version="0.5.3" />
</packages>

View File

@@ -328,7 +328,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
}
catch (ObjectDisposedException)
{
// Ignore
// If ObjectDisposedException was thrown, then execution has already exited the
// "using" statment and source was disposed, meaning that the openTask completed
// successfully. This results in a ObjectDisposedException when trying to access
// source.Token and should be ignored.
}
});
@@ -720,7 +723,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
}
// open connection based on request details
result = await Instance.Connect(connectParams);
result = await Connect(connectParams);
await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result);
}
catch (Exception ex)
@@ -745,7 +748,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
try
{
bool result = Instance.CancelConnect(cancelParams);
bool result = CancelConnect(cancelParams);
await requestContext.SendResult(result);
}
catch(Exception ex)

View File

@@ -55,7 +55,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
try
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString);
Debug.Assert(builder.Pooling == false, "Pooling should be false");
Debug.Assert(!builder.Pooling, "Pooling should be false");
}
catch (Exception ex)
{
@@ -63,7 +63,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
}
#endif
if (AmbientSettings.AlwaysRetryOnTransientFailure == true)
if (AmbientSettings.AlwaysRetryOnTransientFailure)
{
useRetry = true;
}
@@ -368,8 +368,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
IDbCommand cmd = null;
try
{
Debug.Assert(conn.State == ConnectionState.Open, "connection passed to ExecuteReader should be open.");
cmd = conn.CreateCommand();
if (initializeCommand == null)
@@ -603,11 +601,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
Validate.IsNotNullOrEmptyString(nameof(commandText), commandText);
string filePath = ExecuteScalar(conn, commandText, initializeCommand, catchException) as string;
if (!String.IsNullOrWhiteSpace(filePath))
if (!string.IsNullOrWhiteSpace(filePath))
{
// Remove filename from the filePath
Uri pathUri;
if (Uri.TryCreate(filePath, UriKind.Absolute, out pathUri) == false)
if (!Uri.TryCreate(filePath, UriKind.Absolute, out pathUri))
{
// Invalid Uri
return null;
@@ -931,7 +929,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
serverInfo = GetServerVersion(connection);
tempDatabaseName = (String.IsNullOrEmpty(builder.InitialCatalog) == false) ?
tempDatabaseName = (!string.IsNullOrEmpty(builder.InitialCatalog)) ?
builder.InitialCatalog : builder.AttachDBFilename;
// If at this point the dbName remained an empty string then

View File

@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Credentials
/// </summary>
internal CredentialService(ICredentialStore store, StoreConfig config)
{
this.credStore = store != null ? store : GetStoreForOS(config);
credStore = store != null ? store : GetStoreForOS(config);
}
/// <summary>

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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>();
}
}

View File

@@ -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>();
}
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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, "", ","));
}
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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");
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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); }
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -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; }
}
}
}

View File

@@ -0,0 +1,85 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -0,0 +1,46 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.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);
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -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)
{
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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)
);
}
}
}

View 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);
}
}
}
}

View File

@@ -21,6 +21,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Contracts
public bool? DocumentHighlightProvider { get; set; }
public bool? DocumentFormattingProvider { get; set; }
public bool? DocumentRangeFormattingProvider { get; set; }
public bool? DocumentSymbolProvider { get; set; }
public bool? WorkspaceSymbolProvider { get; set; }

View File

@@ -0,0 +1,71 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using Microsoft.SqlTools.ServiceLayer.Extensibility;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
namespace Microsoft.SqlTools.ServiceLayer.Hosting
{
/// <summary>
/// Defines a hosted service that communicates with external processes via
/// messages passed over the <see cref="ServiceHost"/>. The service defines
/// a standard initialization method where it can hook up to the host.
/// </summary>
public interface IHostedService
{
/// <summary>
/// Callback to initialize this service
/// </summary>
/// <param name="serviceHost"><see cref="IProtocolEndpoint"/> which supports registering
/// event handlers and other callbacks for messages passed to external callers</param>
void InitializeService(IProtocolEndpoint serviceHost);
/// <summary>
/// What is the service type that you wish to register?
/// </summary>
Type ServiceType { get; }
}
/// <summary>
/// Base class for <see cref="IHostedService"/> implementations that handles defining the <see cref="ServiceType"/>
/// being registered. This simplifies service registration. This also implements <see cref="IComposableService"/> which
/// allows injection of the service provider for lookup of other services.
///
/// Extending classes should implement per below code example
/// <code>
/// [Export(typeof(IHostedService)]
/// MyService : HostedService&lt;MyService&gt;
/// {
/// 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);
}
}

View File

@@ -19,6 +19,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
/// </summary>
public class ProtocolEndpoint : IProtocolEndpoint
{
private bool isInitialized;
private bool isStarted;
private int currentMessageId;
private ChannelBase protocolChannel;
@@ -64,12 +65,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
}
/// <summary>
/// Starts the language server client and sends the Initialize method.
/// Initializes
/// </summary>
/// <returns>A Task that can be awaited for initialization to complete.</returns>
public async Task Start()
public void Initialize()
{
if (!this.isStarted)
if (!this.isInitialized)
{
// Start the provided protocol channel
this.protocolChannel.Start(this.messageProtocolType);
@@ -83,6 +83,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
// Listen for unhandled exceptions from the dispatcher
this.MessageDispatcher.UnhandledException += MessageDispatcher_UnhandledException;
this.isInitialized = true;
}
}
/// <summary>
/// Starts the language server client and sends the Initialize method.
/// </summary>
/// <returns>A Task that can be awaited for initialization to complete.</returns>
public async Task Start()
{
if (!this.isStarted)
{
// Notify implementation about endpoint start
await this.OnStart();

View File

@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
/// <summary>
/// Provide initialization that must occur after the service host is started
/// </summary>
public void Initialize()
public void InitializeRequestHandlers()
{
// Register the requests that this service host will handle
this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
@@ -153,6 +153,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
TextDocumentSync = TextDocumentSyncKind.Incremental,
DefinitionProvider = true,
ReferencesProvider = false,
DocumentFormattingProvider = true,
DocumentRangeFormattingProvider = true,
DocumentHighlightProvider = false,
HoverProvider = true,
CompletionProvider = new CompletionOptions

View File

@@ -123,6 +123,28 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
}
}
/// <summary>
/// Remove the binding queue entry
/// </summary>
protected void RemoveBindingContext(string key)
{
lock (this.bindingContextLock)
{
if (this.BindingContextMap.ContainsKey(key))
{
// disconnect existing connection
var bindingContext = this.BindingContextMap[key];
if (bindingContext.ServerConnection != null && bindingContext.ServerConnection.IsOpen)
{
bindingContext.ServerConnection.Disconnect();
}
// remove key from the map
this.BindingContextMap.Remove(key);
}
}
}
private bool HasPendingQueueItems
{
get

View File

@@ -51,8 +51,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// <summary>
/// Use a ConnectionInfo item to create a connected binding context
/// </summary>
/// <param name="connInfo"></param>
public virtual string AddConnectionContext(ConnectionInfo connInfo)
/// <param name="connInfo">Connection info used to create binding context</param>
/// <param name="overwrite">Overwrite existing context</param>
public virtual string AddConnectionContext(ConnectionInfo connInfo, bool overwrite = false)
{
if (connInfo == null)
{
@@ -63,8 +64,15 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
string connectionKey = GetConnectionContextKey(connInfo);
if (BindingContextExists(connectionKey))
{
// no need to populate the context again since the context already exists
return connectionKey;
if (overwrite)
{
RemoveBindingContext(connectionKey);
}
else
{
// no need to populate the context again since the context already exists
return connectionKey;
}
}
IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey);

View File

@@ -45,14 +45,6 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
Reference = 18
}
[DebuggerDisplay("NewText = {NewText}, Range = {Range.Start.Line}:{Range.Start.Character} - {Range.End.Line}:{Range.End.Character}")]
public class TextEdit
{
public Range Range { get; set; }
public string NewText { get; set; }
}
[DebuggerDisplay("Kind = {Kind.ToString()}, Label = {Label}, Detail = {Detail}")]
public class CompletionItem
{

View File

@@ -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");
}
}

View File

@@ -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; }
}
}

View File

@@ -212,6 +212,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
serviceHost.SetRequestHandler(HoverRequest.Type, HandleHoverRequest);
serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
serviceHost.SetRequestHandler(DefinitionRequest.Type, HandleDefinitionRequest);
serviceHost.SetEventHandler(RebuildIntelliSenseNotification.Type, HandleRebuildIntelliSenseNotification);
// Register a no-op shutdown task for validation of the shutdown logic
serviceHost.RegisterShutdownTask(async (shutdownParams, shutdownRequestContext) =>
@@ -442,6 +443,62 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
await Task.FromResult(true);
}
/// <summary>
/// Handle the rebuild IntelliSense cache notification
/// </summary>
public async Task HandleRebuildIntelliSenseNotification(
RebuildIntelliSenseParams configChangeParams,
EventContext eventContext)
{
Logger.Write(LogLevel.Verbose, "HandleRebuildIntelliSenseNotification");
// Skip closing this file if the file doesn't exist
var scriptFile = this.CurrentWorkspace.GetFile(configChangeParams.OwnerUri);
if (scriptFile == null)
{
return;
}
ConnectionInfo connInfo;
LanguageService.ConnectionServiceInstance.TryFindConnection(
scriptFile.ClientFilePath,
out connInfo);
await Task.Run(() =>
{
ScriptParseInfo scriptInfo = GetScriptParseInfo(connInfo.OwnerUri, createIfNotExists: false);
if (scriptInfo != null && scriptInfo.IsConnected &&
Monitor.TryEnter(scriptInfo.BuildingMetadataLock, LanguageService.OnConnectionWaitTimeout))
{
try
{
this.BindingQueue.AddConnectionContext(connInfo, overwrite: true);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error, "Unknown error " + ex.ToString());
}
finally
{
// Set Metadata Build event to Signal state.
Monitor.Exit(scriptInfo.BuildingMetadataLock);
}
}
// if not in the preview window and diagnostics are enabled then run diagnostics
if (!IsPreviewWindow(scriptFile)
&& WorkspaceService<SqlToolsSettings>.Instance.CurrentSettings.IsDiagnositicsEnabled)
{
RunScriptDiagnostics(
new ScriptFile[] { scriptFile },
eventContext);
}
// Send a notification to signal that autocomplete is ready
ServiceHost.Instance.SendEvent(IntelliSenseReadyNotification.Type, new IntelliSenseReadyParams() {OwnerUri = connInfo.OwnerUri});
});
}
/// <summary>
/// Handle the file configuration change notification
/// </summary>

View File

@@ -8,7 +8,7 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.SqlTools.ServiceLayer {
namespace Microsoft.SqlTools.ServiceLayer.Localization {
using System;
using System.Reflection;
@@ -38,7 +38,7 @@ namespace Microsoft.SqlTools.ServiceLayer {
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.SqlTools.ServiceLayer.sr", typeof(sr).GetTypeInfo().Assembly);
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.SqlTools.ServiceLayer.Localization.sr", typeof(sr).GetTypeInfo().Assembly);
resourceMan = temp;
}
return resourceMan;
@@ -446,6 +446,24 @@ namespace Microsoft.SqlTools.ServiceLayer {
}
}
/// <summary>
/// Looks up a localized string similar to Replacement of an empty string by an empty string..
/// </summary>
public static string ErrorEmptyStringReplacement {
get {
return ResourceManager.GetString("ErrorEmptyStringReplacement", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot convert SqlCodeObject Type {0} to Type {1}.
/// </summary>
public static string ErrorUnexpectedCodeObjectType {
get {
return ResourceManager.GetString("ErrorUnexpectedCodeObjectType", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Message header must separate key and value using &apos;:&apos;.
/// </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&lt;{1}&gt;.
/// </summary>
public static string IncompatibleServiceForExtensionLoader {
get {
return ResourceManager.GetString("IncompatibleServiceForExtensionLoader", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Multiple services found for type {0}, expected only 1.
/// </summary>
public static string MultipleServicesFound {
get {
return ResourceManager.GetString("MultipleServicesFound", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This feature is currently not supported on Azure SQL DB and Data Warehouse: {0}.
/// </summary>
@@ -833,6 +869,42 @@ namespace Microsoft.SqlTools.ServiceLayer {
}
}
/// <summary>
/// Looks up a localized string similar to Cannot register service for type {0}, one or more services already registered.
/// </summary>
public static string ServiceAlreadyRegistered {
get {
return ResourceManager.GetString("ServiceAlreadyRegistered", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Service {0} was not found in the service provider.
/// </summary>
public static string ServiceNotFound {
get {
return ResourceManager.GetString("ServiceNotFound", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Service of Type {0} is not compatible with registered Type {1}.
/// </summary>
public static string ServiceNotOfExpectedType {
get {
return ResourceManager.GetString("ServiceNotOfExpectedType", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to SetServiceProvider() was not called to establish the required service provider.
/// </summary>
public static string ServiceProviderNotSet {
get {
return ResourceManager.GetString("ServiceProviderNotSet", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to EN_LOCALIZATION.
/// </summary>

View File

@@ -117,6 +117,62 @@ namespace Microsoft.SqlTools.ServiceLayer
}
}
public static string ServiceAlreadyRegistered
{
get
{
return Keys.GetString(Keys.ServiceAlreadyRegistered);
}
}
public static string MultipleServicesFound
{
get
{
return Keys.GetString(Keys.MultipleServicesFound);
}
}
public static string IncompatibleServiceForExtensionLoader
{
get
{
return Keys.GetString(Keys.IncompatibleServiceForExtensionLoader);
}
}
public static string ServiceProviderNotSet
{
get
{
return Keys.GetString(Keys.ServiceProviderNotSet);
}
}
public static string ServiceNotFound
{
get
{
return Keys.GetString(Keys.ServiceNotFound);
}
}
public static string ServiceNotOfExpectedType
{
get
{
return Keys.GetString(Keys.ServiceNotOfExpectedType);
}
}
public static string ErrorUnexpectedCodeObjectType
{
get
{
return Keys.GetString(Keys.ErrorUnexpectedCodeObjectType);
}
}
public static string HostingUnexpectedEndOfStream
{
get
@@ -413,6 +469,14 @@ namespace Microsoft.SqlTools.ServiceLayer
}
}
public static string ErrorEmptyStringReplacement
{
get
{
return Keys.GetString(Keys.ErrorEmptyStringReplacement);
}
}
public static string WorkspaceServicePositionLineOutOfRange
{
get
@@ -724,7 +788,7 @@ namespace Microsoft.SqlTools.ServiceLayer
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Keys
{
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ServiceLayer.SR", typeof(SR).GetTypeInfo().Assembly);
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ServiceLayer.Localization.SR", typeof(SR).GetTypeInfo().Assembly);
static CultureInfo _culture = null;
@@ -774,6 +838,27 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string CredentialServiceWin32CredentialDisposed = "CredentialServiceWin32CredentialDisposed";
public const string ServiceAlreadyRegistered = "ServiceAlreadyRegistered";
public const string MultipleServicesFound = "MultipleServicesFound";
public const string IncompatibleServiceForExtensionLoader = "IncompatibleServiceForExtensionLoader";
public const string ServiceProviderNotSet = "ServiceProviderNotSet";
public const string ServiceNotFound = "ServiceNotFound";
public const string ServiceNotOfExpectedType = "ServiceNotOfExpectedType";
public const string ErrorUnexpectedCodeObjectType = "ErrorUnexpectedCodeObjectType";
public const string HostingUnexpectedEndOfStream = "HostingUnexpectedEndOfStream";
@@ -903,6 +988,9 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string PeekDefinitionTypeNotSupportedError = "PeekDefinitionTypeNotSupportedError";
public const string ErrorEmptyStringReplacement = "ErrorEmptyStringReplacement";
public const string WorkspaceServicePositionLineOutOfRange = "WorkspaceServicePositionLineOutOfRange";

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema

View File

@@ -181,6 +181,34 @@
<value>Win32Credential object is already disposed</value>
<comment></comment>
</data>
<data name="ServiceAlreadyRegistered" xml:space="preserve">
<value>Cannot register service for type {0}, one or more services already registered</value>
<comment></comment>
</data>
<data name="MultipleServicesFound" xml:space="preserve">
<value>Multiple services found for type {0}, expected only 1</value>
<comment></comment>
</data>
<data name="IncompatibleServiceForExtensionLoader" xml:space="preserve">
<value>Service of type {0} cannot be created by ExtensionLoader&lt;{1}&gt;</value>
<comment></comment>
</data>
<data name="ServiceProviderNotSet" xml:space="preserve">
<value>SetServiceProvider() was not called to establish the required service provider</value>
<comment></comment>
</data>
<data name="ServiceNotFound" xml:space="preserve">
<value>Service {0} was not found in the service provider</value>
<comment></comment>
</data>
<data name="ServiceNotOfExpectedType" xml:space="preserve">
<value>Service of Type {0} is not compatible with registered Type {1}</value>
<comment></comment>
</data>
<data name="ErrorUnexpectedCodeObjectType" xml:space="preserve">
<value>Cannot convert SqlCodeObject Type {0} to Type {1}</value>
<comment></comment>
</data>
<data name="HostingUnexpectedEndOfStream" xml:space="preserve">
<value>MessageReader's input stream ended unexpectedly, terminating</value>
<comment></comment>
@@ -359,6 +387,10 @@
<value>This object type is currently not supported by this feature.</value>
<comment></comment>
</data>
<data name="ErrorEmptyStringReplacement" xml:space="preserve">
<value>Replacement of an empty string by an empty string.</value>
<comment></comment>
</data>
<data name="WorkspaceServicePositionLineOutOfRange" xml:space="preserve">
<value>Position is outside of file line range</value>
<comment></comment>

View File

@@ -59,6 +59,26 @@ CredentialsServiceTargetForLookup = Target must be specified to check existance
CredentialServiceWin32CredentialDisposed = Win32Credential object is already disposed
############################################################################
# Extensibility
ServiceAlreadyRegistered = Cannot register service for type {0}, one or more services already registered
MultipleServicesFound = Multiple services found for type {0}, expected only 1
IncompatibleServiceForExtensionLoader = Service of type {0} cannot be created by ExtensionLoader<{1}>
ServiceProviderNotSet = SetServiceProvider() was not called to establish the required service provider
ServiceNotFound = Service {0} was not found in the service provider
ServiceNotOfExpectedType = Service of Type {0} is not compatible with registered Type {1}
############################################################################
# Formatter
ErrorUnexpectedCodeObjectType = Cannot convert SqlCodeObject Type {0} to Type {1}
############################################################################
# Hosting
@@ -168,6 +188,8 @@ PeekDefinitionTimedoutError = Operation timed out.
PeekDefinitionTypeNotSupportedError = This object type is currently not supported by this feature.
ErrorEmptyStringReplacement = Replacement of an empty string by an empty string.
############################################################################
# Workspace Service

View 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&lt;{1}&gt;</source>
<target state="new">Service of type {0} cannot be created by ExtensionLoader&lt;{1}&gt;</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>

View File

@@ -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>

View File

@@ -4,11 +4,6 @@
using System;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Workspace;
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.Credentials;
using Microsoft.SqlTools.ServiceLayer.Utility;
namespace Microsoft.SqlTools.ServiceLayer
@@ -36,25 +31,13 @@ namespace Microsoft.SqlTools.ServiceLayer
Logger.Write(LogLevel.Normal, "Starting SQL Tools Service Host");
// set up the host details and profile paths
var hostDetails = new HostDetails(version: new Version(1,0));
var hostDetails = new HostDetails(version: new Version(1, 0));
SqlToolsContext sqlToolsContext = new SqlToolsContext(hostDetails);
ServiceHost serviceHost = HostLoader.CreateAndStartServiceHost(sqlToolsContext);
// Grab the instance of the service host
ServiceHost serviceHost = ServiceHost.Instance;
// Start the service
serviceHost.Start().Wait();
// Initialize the services that will be hosted here
WorkspaceService<SqlToolsSettings>.Instance.InitializeService(serviceHost);
LanguageService.Instance.InitializeService(serviceHost, sqlToolsContext);
ConnectionService.Instance.InitializeService(serviceHost);
CredentialService.Instance.InitializeService(serviceHost);
QueryExecutionService.Instance.InitializeService(serviceHost);
serviceHost.Initialize();
serviceHost.WaitForExit();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -113,9 +113,9 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlContext
/// </summary>
public SqlToolsSettingsValues()
{
this.IntelliSense = new IntelliSenseSettings();
this.QueryExecutionSettings = new QueryExecutionSettings();
IntelliSense = new IntelliSenseSettings();
QueryExecutionSettings = new QueryExecutionSettings();
Format = new FormatterSettings();
}
/// <summary>
@@ -127,5 +127,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlContext
/// Gets or sets the query execution settings
/// </summary>
public QueryExecutionSettings QueryExecutionSettings { get; set; }
/// <summary>
/// Gets or sets the formatter settings
/// </summary>
[JsonProperty("format")]
public FormatterSettings Format { get; set; }
}
}

View File

@@ -63,7 +63,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
/// <returns>A new FilePosition instance for the calculated position.</returns>
public FilePosition AddOffset(int lineOffset, int columnOffset)
{
return this.scriptFile.CalculatePosition(
return scriptFile.CalculatePosition(
this,
lineOffset,
columnOffset);
@@ -77,7 +77,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
/// <returns>A new FilePosition instance for the calculated position.</returns>
public FilePosition GetLineStart()
{
string scriptLine = scriptFile.FileLines[this.Line - 1];
string scriptLine = scriptFile.FileLines[Line - 1];
int lineStartColumn = 1;
for (int i = 0; i < scriptLine.Length; i++)
@@ -89,7 +89,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
}
}
return new FilePosition(this.scriptFile, this.Line, lineStartColumn);
return new FilePosition(scriptFile, Line, lineStartColumn);
}
/// <summary>
@@ -99,8 +99,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
/// <returns>A new FilePosition instance for the calculated position.</returns>
public FilePosition GetLineEnd()
{
string scriptLine = scriptFile.FileLines[this.Line - 1];
return new FilePosition(this.scriptFile, this.Line, scriptLine.Length + 1);
string scriptLine = scriptFile.FileLines[Line - 1];
return new FilePosition(scriptFile, Line, scriptLine.Length + 1);
}
#endregion

View File

@@ -61,11 +61,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
get
{
return string.Join("\r\n", this.FileLines);
return string.Join("\r\n", FileLines);
}
set
{
this.FileLines = value != null ? value.Split('\n') : null;
FileLines = value != null ? value.Split('\n') : null;
}
}
@@ -118,12 +118,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
string clientFilePath,
TextReader textReader)
{
this.FilePath = filePath;
this.ClientFilePath = clientFilePath;
this.IsAnalysisEnabled = true;
this.IsInMemory = Workspace.IsPathInMemory(filePath);
FilePath = filePath;
ClientFilePath = clientFilePath;
IsAnalysisEnabled = true;
IsInMemory = Workspace.IsPathInMemory(filePath);
this.SetFileContents(textReader.ReadToEnd());
SetFileContents(textReader.ReadToEnd());
}
/// <summary>
@@ -137,11 +137,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
string clientFilePath,
string initialBuffer)
{
this.FilePath = filePath;
this.ClientFilePath = clientFilePath;
this.IsAnalysisEnabled = true;
FilePath = filePath;
ClientFilePath = clientFilePath;
IsAnalysisEnabled = true;
this.SetFileContents(initialBuffer);
SetFileContents(initialBuffer);
}
#endregion
@@ -157,9 +157,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
Validate.IsWithinRange(
"lineNumber", lineNumber,
1, this.FileLines.Count + 1);
1, FileLines.Count + 1);
return this.FileLines[lineNumber - 1];
return FileLines[lineNumber - 1];
}
/// <summary>
/// Gets the text under a specific range
/// </summary>
public string GetTextInRange(BufferRange range)
{
return string.Join(Environment.NewLine, GetLinesInRange(range));
}
/// <summary>
@@ -170,8 +178,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
/// <returns>An array of strings from the specified range of the file.</returns>
public virtual string[] GetLinesInRange(BufferRange bufferRange)
{
this.ValidatePosition(bufferRange.Start);
this.ValidatePosition(bufferRange.End);
ValidatePosition(bufferRange.Start);
ValidatePosition(bufferRange.End);
List<string> linesInRange = new List<string>();
@@ -180,7 +188,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
for (int line = startLine; line <= endLine; line++)
{
string currentLine = this.FileLines[line - 1];
string currentLine = FileLines[line - 1];
int startColumn =
line == startLine
? bufferRange.Start.Column
@@ -208,7 +216,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
/// <param name="bufferPosition">The position in the buffer to be validated.</param>
public void ValidatePosition(BufferPosition bufferPosition)
{
this.ValidatePosition(
ValidatePosition(
bufferPosition.Line,
bufferPosition.Column);
}
@@ -221,14 +229,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
/// <param name="column">The 1-based column to be validated.</param>
public void ValidatePosition(int line, int column)
{
if (line < 1 || line > this.FileLines.Count + 1)
if (line < 1 || line > FileLines.Count + 1)
{
throw new ArgumentOutOfRangeException(nameof(line), SR.WorkspaceServicePositionLineOutOfRange);
}
// The maximum column is either one past the length of the string
// or 1 if the string is empty.
string lineString = this.FileLines[line - 1];
string lineString = FileLines[line - 1];
int maxColumn = lineString.Length > 0 ? lineString.Length + 1 : 1;
if (column < 1 || column > maxColumn)
@@ -243,28 +251,28 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
/// <param name="fileChange">The FileChange to apply to the file's contents.</param>
public void ApplyChange(FileChange fileChange)
{
this.ValidatePosition(fileChange.Line, fileChange.Offset);
this.ValidatePosition(fileChange.EndLine, fileChange.EndOffset);
ValidatePosition(fileChange.Line, fileChange.Offset);
ValidatePosition(fileChange.EndLine, fileChange.EndOffset);
// Break up the change lines
string[] changeLines = fileChange.InsertString.Split('\n');
// Get the first fragment of the first line
string firstLineFragment =
this.FileLines[fileChange.Line - 1]
FileLines[fileChange.Line - 1]
.Substring(0, fileChange.Offset - 1);
// Get the last fragment of the last line
string endLine = this.FileLines[fileChange.EndLine - 1];
string endLine = FileLines[fileChange.EndLine - 1];
string lastLineFragment =
endLine.Substring(
fileChange.EndOffset - 1,
(this.FileLines[fileChange.EndLine - 1].Length - fileChange.EndOffset) + 1);
(FileLines[fileChange.EndLine - 1].Length - fileChange.EndOffset) + 1);
// Remove the old lines
for (int i = 0; i <= fileChange.EndLine - fileChange.Line; i++)
{
this.FileLines.RemoveAt(fileChange.Line - 1);
FileLines.RemoveAt(fileChange.Line - 1);
}
// Build and insert the new lines
@@ -287,7 +295,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
finalLine = finalLine + lastLineFragment;
}
this.FileLines.Insert(currentLineNumber - 1, finalLine);
FileLines.Insert(currentLineNumber - 1, finalLine);
currentLineNumber++;
}
}
@@ -301,7 +309,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
/// <returns>The zero-based offset for the given file position.</returns>
public int GetOffsetAtPosition(int lineNumber, int columnNumber)
{
Validate.IsWithinRange("lineNumber", lineNumber, 1, this.FileLines.Count);
Validate.IsWithinRange("lineNumber", lineNumber, 1, FileLines.Count);
Validate.IsGreaterThan("columnNumber", columnNumber, 0);
int offset = 0;
@@ -316,7 +324,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
else
{
// Add an offset to account for the current platform's newline characters
offset += this.FileLines[i].Length + Environment.NewLine.Length;
offset += FileLines[i].Length + Environment.NewLine.Length;
}
}
@@ -339,9 +347,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
int newLine = originalPosition.Line + lineOffset,
newColumn = originalPosition.Column + columnOffset;
this.ValidatePosition(newLine, newColumn);
ValidatePosition(newLine, newColumn);
string scriptLine = this.FileLines[newLine - 1];
string scriptLine = FileLines[newLine - 1];
newColumn = Math.Min(scriptLine.Length + 1, newColumn);
return new FilePosition(this, newLine, newColumn);
@@ -379,9 +387,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
BufferPosition endPosition = startPosition;
int line = 0;
while (line < this.FileLines.Count)
while (line < FileLines.Count)
{
if (searchedOffset <= currentOffset + this.FileLines[line].Length)
if (searchedOffset <= currentOffset + FileLines[line].Length)
{
int column = searchedOffset - currentOffset;
@@ -415,7 +423,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
}
// Increase the current offset and include newline length
currentOffset += this.FileLines[line].Length + Environment.NewLine.Length;
currentOffset += FileLines[line].Length + Environment.NewLine.Length;
line++;
}
@@ -430,7 +438,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace.Contracts
{
// Split the file contents into lines and trim
// any carriage returns from the strings.
this.FileLines =
FileLines =
fileContents
.Split('\n')
.Select(line => line.TrimEnd('\r'))

View File

@@ -112,6 +112,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
/// List of callbacks to call when a text document is closed
/// </summary>
private List<TextDocCloseCallback> TextDocCloseCallbacks { get; set; }
#endregion
@@ -189,7 +190,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
public void RegisterTextDocOpenCallback(TextDocOpenCallback task)
{
TextDocOpenCallbacks.Add(task);
}
}
#endregion
@@ -287,7 +288,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
var configUpdateTasks = ConfigChangeCallbacks.Select(
t => t(configChangeParams.Settings, CurrentSettings, eventContext));
await Task.WhenAll(configUpdateTasks);
}
}
#endregion

View File

@@ -28,8 +28,15 @@
"NETStandard.Library": "1.6.0",
"Microsoft.NETCore.Runtime.CoreCLR": "1.0.2",
"Microsoft.NETCore.DotNetHostPolicy": "1.0.1",
"Microsoft.DiaSymReader.Native": "1.4.1",
"System.Diagnostics.Process": "4.1.0",
"System.Threading.Thread": "4.0.0",
"System.Runtime.Loader": "4.0.0",
"System.Composition.Runtime": "1.0.31",
"System.Composition.Convention": "1.0.31",
"System.Composition.TypedParts": "1.0.31",
"Microsoft.Extensions.DependencyModel": "1.0.0",
"System.Runtime": "4.3.0",
"Microsoft.DiaSymReader.Native": "1.4.1"
},
"frameworks": {

View File

@@ -113,18 +113,9 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.BatchParser
}
}
static void CopyToOutput(string sourceDirectory, string filename)
{
File.Copy(Path.Combine(sourceDirectory, filename), filename, true);
FileUtilities.SetFileReadWrite(filename);
}
[Fact]
public void BatchParserTest()
{
CopyToOutput(FilesLocation, "TS-err-cycle1.txt");
CopyToOutput(FilesLocation, "cycle2.txt");
Start("err-blockComment");
Start("err-blockComment2");
Start("err-varDefinition");

View File

@@ -135,5 +135,27 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
SignatureHelp signatureHelp = service.GetSignatureHelp(textDocument, result.ScriptFile);
Assert.NotNull(signatureHelp);
}
/// <summary>
/// Test overwriting the binding queue context
/// </summary>
[Fact]
public void OverwriteBindingContext()
{
var result = TestObjects.InitLiveConnectionInfo();
// add a new connection context
var connectionKey = LanguageService.Instance.BindingQueue.AddConnectionContext(result.ConnectionInfo, overwrite: true);
Assert.True(LanguageService.Instance.BindingQueue.BindingContextMap.ContainsKey(connectionKey));
// cache the server connection
var orgServerConnection = LanguageService.Instance.BindingQueue.BindingContextMap[connectionKey].ServerConnection;
Assert.NotNull(orgServerConnection);
// add a new connection context
connectionKey = LanguageService.Instance.BindingQueue.AddConnectionContext(result.ConnectionInfo, overwrite: true);
Assert.True(LanguageService.Instance.BindingQueue.BindingContextMap.ContainsKey(connectionKey));
Assert.False(object.ReferenceEquals(LanguageService.Instance.BindingQueue.BindingContextMap[connectionKey].ServerConnection, orgServerConnection));
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -1,3 +1,4 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
@@ -5,10 +6,15 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
{
/// <summary>
/// Contains environment information needed when running tests.
/// </summary>
public class RunEnvironmentInfo
{
private static string cachedTestFolderPath;
@@ -30,7 +36,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
public static string GetTestDataLocation()
{
string testFolderPath;
string testPath = @"test\Microsoft.SqlTools.ServiceLayer.Test.Common\TestData";
string testPath = Path.Combine("test", "Microsoft.SqlTools.ServiceLayer.Test.Common", "TestData");
string projectPath = Environment.GetEnvironmentVariable(Constants.ProjectPath);
if (projectPath != null)
@@ -45,13 +51,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
}
else
{
string defaultPath = Path.Combine(typeof(Scripts).GetTypeInfo().Assembly.Location, @"..\..\..\..\..");
testFolderPath = Path.Combine(defaultPath, @"Microsoft.SqlTools.ServiceLayer.Test.Common\TestData");
// We are running tests locally, which means we expect to be running inside the bin\debug\netcoreapp directory
// Test Files should be found at the root of the project so go back the necessary number of directories for this
// to be found. We are manually specifying the testFolderPath here for clarity on where to expect this
string assemblyDir = Path.GetDirectoryName(typeof(Scripts).GetTypeInfo().Assembly.Location);
string defaultPath = Path.Combine(assemblyDir, GoUpNDirectories(4));
testFolderPath = Path.Combine(defaultPath, "Microsoft.SqlTools.ServiceLayer.Test.Common", "TestData");
cachedTestFolderPath = testFolderPath;
}
}
return testFolderPath;
}
private static string GoUpNDirectories(int n)
{
string up = ".." + (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "\\" : "/");
return string.Concat(Enumerable.Repeat(up, n));
}
}
}

View File

@@ -78,7 +78,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
return Enumerable.Empty<TestServerIdentity>();
}
}
catch (Exception ex)
catch (Exception)
{
return Enumerable.Empty<TestServerIdentity>();
}

View File

@@ -0,0 +1,5 @@
select *
from mytable
intersect
select *
from mytable

View File

@@ -0,0 +1,15 @@
select *
from mytable
union
select *
from mytable
union all
select *
from mytable
except
select *
from mytable
intersect
select *
from mytable

View File

@@ -0,0 +1,15 @@
seLect *
from mytable
unIon
selECT *
fROM mytable
union ALL
select *
from mytable
excepT
select *
from mytable
Intersect
select *
from mytable

View File

@@ -0,0 +1,15 @@
SELECT *
FROM mytable
UNION
SELECT *
FROM mytable
UNION ALL
SELECT *
FROM mytable
EXCEPT
SELECT *
FROM mytable
INTERSECT
SELECT *
FROM mytable

View File

@@ -0,0 +1,14 @@
select *
from mytable
union
select *
from mytable
except
select *
from mytable
union all
select *
from mytable
intersect
select *
from mytable

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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