Fixed autocomplete bugs (#110)

Autocomplete for items with special characters doesn't add [ ]
Autocomplete default list shows when it shouldn't..like in comments or literals
This commit is contained in:
Leila Lali
2016-10-21 15:03:48 -07:00
committed by GitHub
parent f35b9fda27
commit b3d793dc85
3 changed files with 140 additions and 45 deletions

View File

@@ -157,7 +157,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting
CompletionProvider = new CompletionOptions CompletionProvider = new CompletionOptions
{ {
ResolveProvider = true, ResolveProvider = true,
TriggerCharacters = new string[] { ".", "-", ":", "\\", ",", " " } TriggerCharacters = new string[] { ".", "-", ":", "\\", "," }
}, },
SignatureHelpProvider = new SignatureHelpOptions SignatureHelpProvider = new SignatureHelpOptions
{ {

View File

@@ -4,6 +4,9 @@
// //
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using Microsoft.SqlServer.Management.SqlParser.Binder; using Microsoft.SqlServer.Management.SqlParser.Binder;
using Microsoft.SqlServer.Management.SqlParser.Intellisense; using Microsoft.SqlServer.Management.SqlParser.Intellisense;
@@ -26,6 +29,8 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
private static WorkspaceService<SqlToolsSettings> workspaceServiceInstance; private static WorkspaceService<SqlToolsSettings> workspaceServiceInstance;
private static Regex ValidSqlNameRegex = new Regex(@"^[\p{L}_][\p{L}\p{N}@$#_]{0,127}$");
private static readonly string[] DefaultCompletionText = new string[] private static readonly string[] DefaultCompletionText = new string[]
{ {
"absolute", "absolute",
@@ -484,14 +489,44 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
int startColumn, int startColumn,
int endColumn) int endColumn)
{ {
return new CompletionItem() return CreateCompletionItem(label, label + " keyword", label, CompletionItemKind.Keyword, row, startColumn, endColumn);
}
internal static CompletionItem[] AddTokenToItems(CompletionItem[] currentList, Token token, int row,
int startColumn,
int endColumn)
{
if (currentList != null &&
token != null && !string.IsNullOrWhiteSpace(token.Text) &&
token.Text.All(ch => char.IsLetter(ch)) &&
currentList.All(x => string.Compare(x.Label, token.Text, true) != 0
))
{
var list = currentList.ToList();
list.Insert(0, CreateCompletionItem(token.Text, token.Text, token.Text, CompletionItemKind.Text, row, startColumn, endColumn));
return list.ToArray();
}
return currentList;
}
private static CompletionItem CreateCompletionItem(
string label,
string detail,
string insertText,
CompletionItemKind kind,
int row,
int startColumn,
int endColumn)
{
CompletionItem item = new CompletionItem()
{ {
Label = label, Label = label,
Kind = CompletionItemKind.Keyword, Kind = kind,
Detail = label + " keyword", Detail = detail,
InsertText = insertText,
TextEdit = new TextEdit TextEdit = new TextEdit
{ {
NewText = label, NewText = insertText,
Range = new Range Range = new Range
{ {
Start = new Position Start = new Position
@@ -507,6 +542,8 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
} }
}; };
return item;
} }
/// <summary> /// <summary>
@@ -523,38 +560,56 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
int startColumn, int startColumn,
int endColumn) int endColumn)
{ {
List<CompletionItem> completions = new List<CompletionItem>(); List<CompletionItem> completions = new List<CompletionItem>();
foreach (var autoCompleteItem in suggestions) foreach (var autoCompleteItem in suggestions)
{ {
// convert the completion item candidates into CompletionItems string insertText = GetCompletionItemInsertName(autoCompleteItem);
completions.Add(new CompletionItem() CompletionItemKind kind = CompletionItemKind.Variable;
switch (autoCompleteItem.Type)
{ {
Label = autoCompleteItem.Title, case DeclarationType.Schema:
Kind = CompletionItemKind.Variable, kind = CompletionItemKind.Module;
Detail = autoCompleteItem.Title, break;
TextEdit = new TextEdit case DeclarationType.Column:
{ kind = CompletionItemKind.Field;
NewText = autoCompleteItem.Title, break;
Range = new Range case DeclarationType.Table:
{ kind = CompletionItemKind.Method;
Start = new Position break;
{ case DeclarationType.Database:
Line = row, kind = CompletionItemKind.File;
Character = startColumn break;
}, case DeclarationType.Server:
End = new Position kind = CompletionItemKind.Value;
{ break;
Line = row, default:
Character = endColumn kind = CompletionItemKind.Variable;
} break;
} }
}
});
// convert the completion item candidates into CompletionItems
completions.Add(CreateCompletionItem(autoCompleteItem.Title, autoCompleteItem.Title, insertText, kind, row, startColumn, endColumn));
} }
return completions.ToArray(); return completions.ToArray();
} }
private static string GetCompletionItemInsertName(Declaration autoCompleteItem)
{
string insertText = autoCompleteItem.Title;
if (!string.IsNullOrEmpty(autoCompleteItem.Title) && !ValidSqlNameRegex.IsMatch(autoCompleteItem.Title))
{
insertText = string.Format(CultureInfo.InvariantCulture, "[{0}]", autoCompleteItem.Title);
}
return insertText;
}
/// <summary> /// <summary>
/// Preinitialize the parser and binder with common metadata. /// Preinitialize the parser and binder with common metadata.
/// This should front load the long binding wait to the time the /// This should front load the long binding wait to the time the

View File

@@ -264,10 +264,10 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
var completionItems = Instance.GetCompletionItems( var completionItems = Instance.GetCompletionItems(
textDocumentPosition, scriptFile, connInfo); textDocumentPosition, scriptFile, connInfo);
await requestContext.SendResult(completionItems); await requestContext.SendResult(completionItems);
}
} }
}
/// <summary> /// <summary>
/// Handle the resolve completion request event to provide additional /// Handle the resolve completion request event to provide additional
@@ -686,12 +686,17 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
bool useLowerCaseSuggestions = this.CurrentSettings.SqlTools.IntelliSense.LowerCaseSuggestions.Value; bool useLowerCaseSuggestions = this.CurrentSettings.SqlTools.IntelliSense.LowerCaseSuggestions.Value;
this.currentCompletionParseInfo = null; this.currentCompletionParseInfo = null;
CompletionItem[] defaultCompletionItems = AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn, useLowerCaseSuggestions);
CompletionItem[] resultCompletionItems = defaultCompletionItems;
CompletionItem[] emptyCompletionItems = new CompletionItem[0];
int line = textDocumentPosition.Position.Line + 1;
int column = textDocumentPosition.Position.Character + 1;
// get the current script parse info object // get the current script parse info object
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri); ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri);
if (connInfo == null || scriptParseInfo == null) if (connInfo == null || scriptParseInfo == null)
{ {
return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn, useLowerCaseSuggestions); return defaultCompletionItems;
} }
// reparse and bind the SQL statement if needed // reparse and bind the SQL statement if needed
@@ -702,8 +707,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
if (scriptParseInfo.ParseResult == null) if (scriptParseInfo.ParseResult == null)
{ {
return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn, useLowerCaseSuggestions); return defaultCompletionItems;
} }
Token token = GetToken(scriptParseInfo, line, column);
if (scriptParseInfo.IsConnected if (scriptParseInfo.IsConnected
&& Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock, LanguageService.FindCompletionStartTimeout)) && Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock, LanguageService.FindCompletionStartTimeout))
@@ -721,42 +727,76 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// get the completion list from SQL Parser // get the completion list from SQL Parser
scriptParseInfo.CurrentSuggestions = Resolver.FindCompletions( scriptParseInfo.CurrentSuggestions = Resolver.FindCompletions(
scriptParseInfo.ParseResult, scriptParseInfo.ParseResult,
textDocumentPosition.Position.Line + 1, line,
textDocumentPosition.Position.Character + 1, column,
bindingContext.MetadataDisplayInfoProvider); bindingContext.MetadataDisplayInfoProvider);
// cache the current script parse info object to resolve completions later // cache the current script parse info object to resolve completions later
this.currentCompletionParseInfo = scriptParseInfo; this.currentCompletionParseInfo = scriptParseInfo;
// convert the suggestion list to the VS Code format // convert the suggestion list to the VS Code format
completions = AutoCompleteHelper.ConvertDeclarationsToCompletionItems( completions = AutoCompleteHelper.ConvertDeclarationsToCompletionItems(
scriptParseInfo.CurrentSuggestions, scriptParseInfo.CurrentSuggestions,
startLine, startLine,
startColumn, startColumn,
endColumn); endColumn
);
return completions; return completions;
}, },
timeoutOperation: (bindingContext) => timeoutOperation: (bindingContext) =>
{ {
return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn, useLowerCaseSuggestions); return defaultCompletionItems;
}); });
queueItem.ItemProcessed.WaitOne(); queueItem.ItemProcessed.WaitOne();
var completionItems = queueItem.GetResultAsT<CompletionItem[]>(); var completionItems = queueItem.GetResultAsT<CompletionItem[]>();
if (completionItems != null && completionItems.Length > 0) if (completionItems != null && completionItems.Length > 0)
{ {
return completionItems; resultCompletionItems = completionItems;
} }
else if (!ShouldShowCompletionList(token))
{
resultCompletionItems = emptyCompletionItems;
}
} }
finally finally
{ {
Monitor.Exit(scriptParseInfo.BuildingMetadataLock); Monitor.Exit(scriptParseInfo.BuildingMetadataLock);
} }
} }
//resultCompletionItems = AutoCompleteHelper.AddTokenToItems(resultCompletionItems, token, startLine, startColumn, endColumn);
return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn, useLowerCaseSuggestions); return resultCompletionItems;
}
private static Token GetToken(ScriptParseInfo scriptParseInfo, int startLine, int startColumn)
{
if (scriptParseInfo != null && scriptParseInfo.ParseResult != null && scriptParseInfo.ParseResult.Script != null && scriptParseInfo.ParseResult.Script.Tokens != null)
{
var tokenIndex = scriptParseInfo.ParseResult.Script.TokenManager.FindToken(startLine, startColumn);
if (tokenIndex >= 0)
{
return scriptParseInfo.ParseResult.Script.Tokens.ToList()[tokenIndex];
}
}
return null;
}
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;
} }
#endregion #endregion