mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-31 17:24:37 -05:00
adding a new event for when definition is requested (#167)
* sending telemetry events for intellisense usage
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// 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.Diagnostics;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion
|
||||
{
|
||||
/// <summary>
|
||||
/// Includes the objects created by auto completion service
|
||||
/// </summary>
|
||||
public class AutoCompletionResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates new instance
|
||||
/// </summary>
|
||||
public AutoCompletionResult()
|
||||
{
|
||||
Stopwatch = new Stopwatch();
|
||||
Stopwatch.Start();
|
||||
}
|
||||
|
||||
private Stopwatch Stopwatch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Completes the results to calculate the duration
|
||||
/// </summary>
|
||||
public void CompleteResult(CompletionItem[] completionItems)
|
||||
{
|
||||
Stopwatch.Stop();
|
||||
CompletionItems = completionItems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of milliseconds to process the result
|
||||
/// </summary>
|
||||
public double Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
return Stopwatch.ElapsedMilliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completion list
|
||||
/// </summary>
|
||||
public CompletionItem[] CompletionItems { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
//
|
||||
// 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.Threading;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
|
||||
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion
|
||||
{
|
||||
/// <summary>
|
||||
/// A service to create auto complete list for given script document
|
||||
/// </summary>
|
||||
internal class CompletionService
|
||||
{
|
||||
private ConnectedBindingQueue BindingQueue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Created new instance given binding queue
|
||||
/// </summary>
|
||||
public CompletionService(ConnectedBindingQueue bindingQueue)
|
||||
{
|
||||
BindingQueue = bindingQueue;
|
||||
}
|
||||
|
||||
private ISqlParserWrapper sqlParserWrapper;
|
||||
|
||||
/// <summary>
|
||||
/// SQL parser wrapper to create the completion list
|
||||
/// </summary>
|
||||
public ISqlParserWrapper SqlParserWrapper
|
||||
{
|
||||
get
|
||||
{
|
||||
if(this.sqlParserWrapper == null)
|
||||
{
|
||||
this.sqlParserWrapper = new SqlParserWrapper();
|
||||
}
|
||||
return this.sqlParserWrapper;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.sqlParserWrapper = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a completion list given connection and document info
|
||||
/// </summary>
|
||||
public AutoCompletionResult CreateCompletions(
|
||||
ConnectionInfo connInfo,
|
||||
ScriptDocumentInfo scriptDocumentInfo,
|
||||
bool useLowerCaseSuggestions)
|
||||
{
|
||||
AutoCompletionResult result = new AutoCompletionResult();
|
||||
// check if the file is connected and the file lock is available
|
||||
if (scriptDocumentInfo.ScriptParseInfo.IsConnected && Monitor.TryEnter(scriptDocumentInfo.ScriptParseInfo.BuildingMetadataLock))
|
||||
{
|
||||
try
|
||||
{
|
||||
QueueItem queueItem = AddToQueue(connInfo, scriptDocumentInfo.ScriptParseInfo, scriptDocumentInfo, useLowerCaseSuggestions);
|
||||
|
||||
// wait for the queue item
|
||||
queueItem.ItemProcessed.WaitOne();
|
||||
var completionResult = queueItem.GetResultAsT<AutoCompletionResult>();
|
||||
if (completionResult != null && completionResult.CompletionItems != null && completionResult.CompletionItems.Length > 0)
|
||||
{
|
||||
result = completionResult;
|
||||
}
|
||||
else if (!ShouldShowCompletionList(scriptDocumentInfo.Token))
|
||||
{
|
||||
result.CompleteResult(AutoCompleteHelper.EmptyCompletionList);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(scriptDocumentInfo.ScriptParseInfo.BuildingMetadataLock);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private QueueItem AddToQueue(
|
||||
ConnectionInfo connInfo,
|
||||
ScriptParseInfo scriptParseInfo,
|
||||
ScriptDocumentInfo scriptDocumentInfo,
|
||||
bool useLowerCaseSuggestions)
|
||||
{
|
||||
// queue the completion task with the binding queue
|
||||
QueueItem queueItem = this.BindingQueue.QueueBindingOperation(
|
||||
key: scriptParseInfo.ConnectionKey,
|
||||
bindingTimeout: LanguageService.BindingTimeout,
|
||||
bindOperation: (bindingContext, cancelToken) =>
|
||||
{
|
||||
return CreateCompletionsFromSqlParser(connInfo, scriptParseInfo, scriptDocumentInfo, bindingContext.MetadataDisplayInfoProvider);
|
||||
},
|
||||
timeoutOperation: (bindingContext) =>
|
||||
{
|
||||
// return the default list if the connected bind fails
|
||||
return CreateDefaultCompletionItems(scriptParseInfo, scriptDocumentInfo, useLowerCaseSuggestions);
|
||||
});
|
||||
return queueItem;
|
||||
}
|
||||
|
||||
private static bool ShouldShowCompletionList(Token token)
|
||||
{
|
||||
bool result = true;
|
||||
if (token != null)
|
||||
{
|
||||
switch (token.Id)
|
||||
{
|
||||
case (int)Tokens.LEX_MULTILINE_COMMENT:
|
||||
case (int)Tokens.LEX_END_OF_LINE_COMMENT:
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private AutoCompletionResult CreateDefaultCompletionItems(ScriptParseInfo scriptParseInfo, ScriptDocumentInfo scriptDocumentInfo, bool useLowerCaseSuggestions)
|
||||
{
|
||||
AutoCompletionResult result = new AutoCompletionResult();
|
||||
CompletionItem[] completionList = AutoCompleteHelper.GetDefaultCompletionItems(scriptDocumentInfo, useLowerCaseSuggestions);
|
||||
result.CompleteResult(completionList);
|
||||
return result;
|
||||
}
|
||||
|
||||
private AutoCompletionResult CreateCompletionsFromSqlParser(
|
||||
ConnectionInfo connInfo,
|
||||
ScriptParseInfo scriptParseInfo,
|
||||
ScriptDocumentInfo scriptDocumentInfo,
|
||||
MetadataDisplayInfoProvider metadataDisplayInfoProvider)
|
||||
{
|
||||
AutoCompletionResult result = new AutoCompletionResult();
|
||||
IEnumerable<Declaration> suggestions = SqlParserWrapper.FindCompletions(
|
||||
scriptParseInfo.ParseResult,
|
||||
scriptDocumentInfo.ParserLine,
|
||||
scriptDocumentInfo.ParserColumn,
|
||||
metadataDisplayInfoProvider);
|
||||
|
||||
// get the completion list from SQL Parser
|
||||
scriptParseInfo.CurrentSuggestions = suggestions;
|
||||
|
||||
// convert the suggestion list to the VS Code format
|
||||
CompletionItem[] completionList = AutoCompleteHelper.ConvertDeclarationsToCompletionItems(
|
||||
scriptParseInfo.CurrentSuggestions,
|
||||
scriptDocumentInfo.StartLine,
|
||||
scriptDocumentInfo.StartColumn,
|
||||
scriptDocumentInfo.EndColumn,
|
||||
scriptDocumentInfo.TokenText);
|
||||
|
||||
result.CompleteResult(completionList);
|
||||
|
||||
//The bucket for number of milliseconds will take to send back auto complete list
|
||||
connInfo.IntellisenseMetrics.UpdateMetrics(result.Duration, 1, (k2, v2) => v2 + 1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a completion item from SQL parser declaration item
|
||||
/// </summary>
|
||||
public class SqlCompletionItem
|
||||
{
|
||||
private static Regex ValidSqlNameRegex = new Regex(@"^[\p{L}_@][\p{L}\p{N}@$#_]{0,127}$");
|
||||
|
||||
/// <summary>
|
||||
/// Create new instance given the SQL parser declaration
|
||||
/// </summary>
|
||||
public SqlCompletionItem(Declaration declaration, string tokenText) :
|
||||
this(declaration == null ? null : declaration.Title, declaration == null ? DeclarationType.Table : declaration.Type, tokenText)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new instance given declaration title and type
|
||||
/// </summary>
|
||||
public SqlCompletionItem(string declarationTitle, DeclarationType declarationType, string tokenText)
|
||||
{
|
||||
Validate.IsNotNullOrEmptyString("declarationTitle", declarationTitle);
|
||||
|
||||
DeclarationTitle = declarationTitle;
|
||||
DeclarationType = declarationType;
|
||||
TokenText = tokenText;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
InsertText = GetCompletionItemInsertName();
|
||||
Label = DeclarationTitle;
|
||||
if (StartsWithBracket(TokenText))
|
||||
{
|
||||
Label = WithBracket(Label);
|
||||
InsertText = WithBracket(InsertText);
|
||||
}
|
||||
Detail = Label;
|
||||
Kind = CreateCompletionItemKind();
|
||||
}
|
||||
|
||||
private CompletionItemKind CreateCompletionItemKind()
|
||||
{
|
||||
CompletionItemKind kind = CompletionItemKind.Variable;
|
||||
switch (DeclarationType)
|
||||
{
|
||||
case DeclarationType.Schema:
|
||||
kind = CompletionItemKind.Module;
|
||||
break;
|
||||
case DeclarationType.Column:
|
||||
kind = CompletionItemKind.Field;
|
||||
break;
|
||||
case DeclarationType.Table:
|
||||
case DeclarationType.View:
|
||||
kind = CompletionItemKind.File;
|
||||
break;
|
||||
case DeclarationType.Database:
|
||||
kind = CompletionItemKind.Method;
|
||||
break;
|
||||
case DeclarationType.ScalarValuedFunction:
|
||||
case DeclarationType.TableValuedFunction:
|
||||
case DeclarationType.BuiltInFunction:
|
||||
kind = CompletionItemKind.Value;
|
||||
break;
|
||||
default:
|
||||
kind = CompletionItemKind.Unit;
|
||||
break;
|
||||
}
|
||||
|
||||
return kind;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declaration Title
|
||||
/// </summary>
|
||||
public string DeclarationTitle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Token text from the editor
|
||||
/// </summary>
|
||||
public string TokenText { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SQL declaration type
|
||||
/// </summary>
|
||||
public DeclarationType DeclarationType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Completion item label
|
||||
/// </summary>
|
||||
public string Label { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Completion item kind
|
||||
/// </summary>
|
||||
public CompletionItemKind Kind { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Completion insert text
|
||||
/// </summary>
|
||||
public string InsertText { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Completion item detail
|
||||
/// </summary>
|
||||
public string Detail { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a completion item given the editor info
|
||||
/// </summary>
|
||||
public CompletionItem CreateCompletionItem(
|
||||
int row,
|
||||
int startColumn,
|
||||
int endColumn)
|
||||
{
|
||||
return CreateCompletionItem(Label, Detail, InsertText, Kind, row, startColumn, endColumn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a completion item
|
||||
/// </summary>
|
||||
public static CompletionItem CreateCompletionItem(
|
||||
string label,
|
||||
string detail,
|
||||
string insertText,
|
||||
CompletionItemKind kind,
|
||||
int row,
|
||||
int startColumn,
|
||||
int endColumn)
|
||||
{
|
||||
CompletionItem item = new CompletionItem()
|
||||
{
|
||||
Label = label,
|
||||
Kind = kind,
|
||||
Detail = detail,
|
||||
InsertText = insertText,
|
||||
TextEdit = new TextEdit
|
||||
{
|
||||
NewText = insertText,
|
||||
Range = new Range
|
||||
{
|
||||
Start = new Position
|
||||
{
|
||||
Line = row,
|
||||
Character = startColumn
|
||||
},
|
||||
End = new Position
|
||||
{
|
||||
Line = row,
|
||||
Character = endColumn
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private string GetCompletionItemInsertName()
|
||||
{
|
||||
string insertText = DeclarationTitle;
|
||||
if (!string.IsNullOrEmpty(DeclarationTitle) && !ValidSqlNameRegex.IsMatch(DeclarationTitle))
|
||||
{
|
||||
insertText = WithBracket(DeclarationTitle);
|
||||
}
|
||||
return insertText;
|
||||
}
|
||||
|
||||
private bool HasBrackets(string text)
|
||||
{
|
||||
return text != null && text.StartsWith("[") && text.EndsWith("]");
|
||||
}
|
||||
|
||||
private bool StartsWithBracket(string text)
|
||||
{
|
||||
return text != null && text.StartsWith("[");
|
||||
}
|
||||
|
||||
private string WithBracket(string text)
|
||||
{
|
||||
if (!HasBrackets(text))
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "[{0}]", text);
|
||||
}
|
||||
else
|
||||
{
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// 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 Microsoft.SqlServer.Management.SqlParser.Intellisense;
|
||||
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion
|
||||
{
|
||||
/// <summary>
|
||||
/// SqlParserWrapper interface
|
||||
/// </summary>
|
||||
public interface ISqlParserWrapper
|
||||
{
|
||||
IEnumerable<Declaration> FindCompletions(ParseResult parseResult, int line, int col, IMetadataDisplayInfoProvider displayInfoProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper class around SQL parser methods to make the operations testable
|
||||
/// </summary>
|
||||
public class SqlParserWrapper : ISqlParserWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates completion list given SQL script info
|
||||
/// </summary>
|
||||
public IEnumerable<Declaration> FindCompletions(ParseResult parseResult, int line, int col, IMetadataDisplayInfoProvider displayInfoProvider)
|
||||
{
|
||||
return Resolver.FindCompletions(parseResult, line, col, displayInfoProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user