mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 09:59:48 -05:00
* Copy smoModel some rename * Copy entire service layer * Building copy * Fixing some references * Launch profile * Resolve namespace issues * Compiling tests. Correct manifest. * Fixing localization resources * ReliableKustoClient * Some trimming of extra code and Kusto code * Kusto client creation in bindingContent * Removing Smo and new Kusto classes * More trimming * Kusto schema hookup * Solidying DataSource abstraction * Solidifying further * Latest refatoring * More refactoring * Building and launching Kusto service layer * Working model which enumerates databases * Refactoring to pass IDataSource to all tree nodes * Removing some dependencies on the context * Working with tables and schema * Comment checkin * Refactoring to give out select script * Query created and sent back to ADS * Fix query generation * Fix listing of databases * Tunneling the query through. * Successful query execution * Return only results table * Deleting Cms * Delete DacFx * Delete SchemaCompare and TaskServices * Change build definition to not stop at launch * Fix error after merge * Save Kusto results in different formats (#935) * save results as csv etc * some fixes Co-authored-by: Monica Gupta <mogupt@microsoft.com> * 2407 Added OrderBy clause in KustoDataSource > GetDatabaseMetaData and GetColumnMetadata (#959) * 2405 Defaulted Options when setting ServerInfo in ConnectionService > GetConnectionCompleteParams (#965) * 2747 Fixed IsUnknownType error for Kusto (#989) * 2747 Removed unused directives in Kusto > DbColumnWrapper. Refactored IsUnknownType to handle null DataTypeName * 2747 Reverted IsUnknownType change in DbColumnWrapper. Changed DataTypeName to get calue from ColumnType. Refactored SafeGetValue to type check before hard casting to reduce case exceptions. * Added EmbeddedResourceUseDependentUponConvention to Microsoft.Kusto.ServiceLayer.csproj. Also renamed DACfx to match Microsoft.SqlTools.ServiceLayer. Added to compile Exclude="**/obj/**/*.cs" * Srahman cleanup sql code (#992) * Removed Management and Security Service Code. * Remove FileBrowser service * Comment why we are using SqlServer library * Remove SQL specific type definitions * clean up formatter service (#996) Co-authored-by: Monica Gupta <mogupt@microsoft.com> * Code clean up and Kusto intellisense (#994) * Code clean up and Kusto intellisense * Addressed few comments * Addressed few comments * addressed comments Co-authored-by: Monica Gupta <mogupt@microsoft.com> * Return multiple tables for Kusto * Changes required for Kusto manage dashboard (#1039) * Changes required for manage dashboard * Addressed comments Co-authored-by: Monica Gupta <mogupt@microsoft.com> * 2728 Kusto function support (#1038) * loc update (#914) * loc update * loc updates * 2728 moved ColumnInfo and KustoResultsReader to separate files. Added Folder and Function to TreeNode.cs * 2728 Added FunctionInfo. Added Folder to ColumnInfo. Removed partial class from KustoResultsReader. Set Function.IsAlwaysLeaf=true in TreeNode.cs. In KustoDataSource changed tableMetadata type to TableMetaData. Added folder and function dictionaries. Refactored GetSchema function. Renamed GenerateColumnMetadataKey to GenerateMetadataKey * 2728 Added FunctionInfo. Added Folder to ColumnInfo. Removed partial class from KustoResultsReader. Set Function.IsAlwaysLeaf=true in TreeNode.cs. In KustoDataSource changed tableMetadata type to TableMetaData. Added folder and function dictionaries. Refactored GetSchema function. Renamed GenerateColumnMetadataKey to GenerateMetadataKey * 2728 Created new SqlConnection within using block. Refactored KustoDataSource > columnmetadata to sort on get instead of insert. * 2728 Added GetFunctionInfo function to KustoDataSource. * 2728 Reverted change to Microsoft.Kusto.ServiceLayer.csproj from merge * 2728 Reverted change to SqlTools.ServiceLayer\Localization\transXliff * 2728 Reverted change to sr.de.xlf and sr.zh-hans.xlf * 2728 Refactored KustoDataSource Function folders to support subfolders * 2728 Refactored KustoDataSource to use urn for folders, functions, and tables instead of name. * Merge remote-tracking branch 'origin/main' into feature-ADE # Conflicts: # Packages.props * 2728 Moved metadata files into Metadata subdirectory. Added GenerateAlterFunction to IDataSource and DataSourceBase. * 2728 Added summary information to SafeAdd in SystemExtensions. Renamed local variable in SetTableMetadata * 2728 Moved SafeAdd from SystemExtensions to KustoQueryUtils. Added check when getting database schema to return existing records before querying again. Added AddRange function to KustoQueryUtils. Created SetFolderMetadataForFunctions method. * 2728 Added DatabaseKeyPrefix to only return tables to a database for the dashboard. Added logic to store all database tables within the tableMetadata dictionary for the dashboard. * 2728 Created TableInfo and moved info objects into Models directory. Refactored KustoDataSource to lazy load columns for tables. Refactored logic to load tables using cslschema instead of schema. * 2728 Renamed LoadColumnSchema to GetTableSchema to be consistent. Co-authored-by: khoiph1 <khoiph@microsoft.com> * Addressed comments Co-authored-by: Shafiq Rahman <srahman@microsoft.com> Co-authored-by: Monica Gupta <mogupt@microsoft.com> Co-authored-by: Justin M <63619224+JustinMDotNet@users.noreply.github.com> Co-authored-by: rkselfhost <rkselfhost@outlook.com> Co-authored-by: khoiph1 <khoiph@microsoft.com>
321 lines
12 KiB
C#
321 lines
12 KiB
C#
//
|
|
// 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.IO;
|
|
using System.Threading.Tasks;
|
|
//using Kusto.Language;
|
|
//using Kusto.Language.Editor;
|
|
using Microsoft.SqlTools.Extensibility;
|
|
using Microsoft.SqlTools.Hosting;
|
|
using Microsoft.SqlTools.Hosting.Protocol;
|
|
using Microsoft.Kusto.ServiceLayer.Formatter.Contracts;
|
|
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
|
using Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts;
|
|
using Microsoft.Kusto.ServiceLayer.SqlContext;
|
|
using Microsoft.Kusto.ServiceLayer.Workspace;
|
|
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
|
using Microsoft.SqlTools.Utility;
|
|
using Range = Microsoft.Kusto.ServiceLayer.Workspace.Contracts.Range;
|
|
|
|
namespace Microsoft.Kusto.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)
|
|
{
|
|
Logger.Write(TraceEventType.Verbose, "TSqlFormatter initialized");
|
|
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>
|
|
/// Gets the language service. Note: should handle case where this is null in cases where unit tests do not set this up
|
|
/// </summary>
|
|
private LanguageService LanguageService
|
|
{
|
|
get { return ServiceProvider.GetService<LanguageService>(); }
|
|
}
|
|
|
|
/// <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");
|
|
|
|
DocumentStatusHelper.SendTelemetryEvent(requestContext, CreateTelemetryProps(isDocFormat: true));
|
|
}
|
|
|
|
public async Task HandleDocRangeFormatRequest(DocumentRangeFormattingParams docRangeFormatParams, RequestContext<TextEdit[]> requestContext)
|
|
{
|
|
Func<Task<TextEdit[]>> requestHandler = () =>
|
|
{
|
|
return FormatRangeAndReturnEdits(docRangeFormatParams);
|
|
};
|
|
await HandleRequest(requestHandler, requestContext, "HandleDocRangeFormatRequest");
|
|
|
|
DocumentStatusHelper.SendTelemetryEvent(requestContext, CreateTelemetryProps(isDocFormat: false));
|
|
}
|
|
private static TelemetryProperties CreateTelemetryProps(bool isDocFormat)
|
|
{
|
|
return new TelemetryProperties
|
|
{
|
|
Properties = new Dictionary<string, string>
|
|
{
|
|
{ TelemetryPropertyNames.FormatType,
|
|
isDocFormat ? TelemetryPropertyNames.DocumentFormatType : TelemetryPropertyNames.RangeFormatType }
|
|
},
|
|
EventName = TelemetryEventNames.FormatCode
|
|
};
|
|
}
|
|
|
|
private async Task<TextEdit[]> FormatRangeAndReturnEdits(DocumentRangeFormattingParams docFormatParams)
|
|
{
|
|
return await Task.Factory.StartNew(() =>
|
|
{
|
|
if (ShouldSkipFormatting(docFormatParams))
|
|
{
|
|
return Array.Empty<TextEdit>();
|
|
}
|
|
|
|
var range = docFormatParams.Range;
|
|
ScriptFile scriptFile = GetFile(docFormatParams);
|
|
if (scriptFile == null)
|
|
{
|
|
return new TextEdit[0];
|
|
}
|
|
TextEdit textEdit = new TextEdit { Range = range };
|
|
string text = scriptFile.GetTextInRange(range.ToBufferRange());
|
|
return DoFormat(docFormatParams, textEdit, text);
|
|
});
|
|
}
|
|
|
|
private bool ShouldSkipFormatting(DocumentFormattingParams docFormatParams)
|
|
{
|
|
if (docFormatParams == null
|
|
|| docFormatParams.TextDocument == null
|
|
|| docFormatParams.TextDocument.Uri == null)
|
|
{
|
|
return true;
|
|
}
|
|
return (LanguageService != null && LanguageService.ShouldSkipNonMssqlFile(docFormatParams.TextDocument.Uri));
|
|
}
|
|
|
|
private async Task<TextEdit[]> FormatAndReturnEdits(DocumentFormattingParams docFormatParams)
|
|
{
|
|
return await Task.Factory.StartNew(() =>
|
|
{
|
|
if (ShouldSkipFormatting(docFormatParams))
|
|
{
|
|
return Array.Empty<TextEdit>();
|
|
}
|
|
|
|
var scriptFile = GetFile(docFormatParams);
|
|
if (scriptFile == null
|
|
|| 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(TraceEventType.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;
|
|
//TODOKusto: Implement formatting for Kusto generically here.
|
|
//var kustoCodeService = new KustoCodeService(input, GlobalState.Default);
|
|
//var formattedText = kustoCodeService.GetFormattedText();
|
|
//DoFormat(input, options, verifyOutput, visitor =>
|
|
//{
|
|
//result = formattedText.Text;
|
|
//});
|
|
|
|
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)
|
|
);
|
|
}
|
|
}
|
|
}
|