Fixes BindingQueue, GetSignatureHelp and RefreshIntellisenseCache to be truly async (#2175)

This commit is contained in:
Cheena Malhotra
2023-08-23 13:46:13 -07:00
committed by GitHub
parent fcd84f242a
commit cea10c13e6
8 changed files with 433 additions and 414 deletions

View File

@@ -177,7 +177,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
requestContext.Verify(x => x.SendError(It.IsAny<string>(), 0, It.IsAny<string>()), Times.Once);
ScriptParseInfo scriptInfo = new ScriptParseInfo { IsConnected = true };
autoCompleteService.ParseAndBind(result.ScriptFile, result.ConnectionInfo);
await autoCompleteService.ParseAndBind(result.ScriptFile, result.ConnectionInfo);
scriptInfo.ConnectionKey = autoCompleteService.BindingQueue.AddConnectionContext(result.ConnectionInfo);
//Invoke auto completion with extension enabled
@@ -219,7 +219,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
/// provide signature help.
/// </summary>
[Test]
public void GetSignatureHelpReturnsNotNullIfParseInfoInitialized()
public async Task GetSignatureHelpReturnsNotNullIfParseInfoInitialized()
{
// When we make a connection to a live database
Hosting.ServiceHost.SendEventIgnoreExceptions = true;
@@ -243,14 +243,14 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
// If the SQL has already been parsed
var service = CreateLanguageService(result.ScriptFile);
service.DoUpdateLanguageServiceOnConnection(result.ConnectionInfo);
await service.UpdateLanguageServiceOnConnection(result.ConnectionInfo);
// We should get back a non-null ScriptParseInfo
ScriptParseInfo? parseInfo = service.GetScriptParseInfo(result.ScriptFile.ClientUri);
Assert.That(parseInfo, Is.Not.Null, "ScriptParseInfo");
// And we should get back a non-null SignatureHelp
SignatureHelp? signatureHelp = service.GetSignatureHelp(textDocument, result.ScriptFile);
SignatureHelp? signatureHelp = await service.GetSignatureHelp(textDocument, result.ScriptFile);
Assert.That(signatureHelp, Is.Not.Null, "SignatureHelp");
}
@@ -319,7 +319,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
testDb.RunQuery("CREATE TABLE dbo.foo(col1 int)");
// And refresh the cache
await langService.HandleRebuildIntelliSenseNotification(
await langService.DoHandleRebuildIntellisenseNotification(
new RebuildIntelliSenseParams() { OwnerUri = connectionInfoResult.ScriptFile.ClientUri },
new TestEventContext());
@@ -446,7 +446,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServer
testDb.RunQuery(createTableQueries);
// And refresh the cache
await langService.HandleRebuildIntelliSenseNotification(
await langService.DoHandleRebuildIntellisenseNotification(
new RebuildIntelliSenseParams() { OwnerUri = connectionInfoResult.ScriptFile.ClientUri },
new TestEventContext());

View File

@@ -259,7 +259,7 @@ GO";
scriptFile.Contents = "select * from dbo.func ()";
ScriptParseInfo scriptInfo = new ScriptParseInfo { IsConnected = true };
languageService.ScriptParseInfoMap.Add(scriptFile.ClientUri, scriptInfo);
languageService.ScriptParseInfoMap.TryAdd(scriptFile.ClientUri, scriptInfo);
// Pass in null connection info to force doing a local parse since that hits the BindingQueue timeout
// before we want it to (this is testing the timeout trying to fetch the definitions after the parse)
@@ -729,9 +729,9 @@ GO";
Thread.Sleep(2000);
ScriptParseInfo scriptInfo = new ScriptParseInfo { IsConnected = true };
service.ParseAndBind(scriptFile, connInfo);
await service.ParseAndBind(scriptFile, connInfo);
scriptInfo.ConnectionKey = bindingQueue.AddConnectionContext(connInfo);
service.ScriptParseInfoMap.Add(OwnerUri, scriptInfo);
service.ScriptParseInfoMap.TryAdd(OwnerUri, scriptInfo);
// When I call the language service
var objectResult = service.GetDefinition(objectDocument, scriptFile, connInfo);
@@ -750,7 +750,7 @@ GO";
Cleanup(objectResult.Locations);
Cleanup(sysResult.Locations);
Cleanup(masterResult.Locations);
service.ScriptParseInfoMap.Remove(OwnerUri);
service.ScriptParseInfoMap.TryRemove(OwnerUri, out _);
connInfo.RemoveAllConnections();
}
@@ -786,9 +786,9 @@ GO";
Thread.Sleep(2000);
ScriptParseInfo scriptInfo = new ScriptParseInfo { IsConnected = true };
service.ParseAndBind(scriptFile, connInfo);
await service.ParseAndBind(scriptFile, connInfo);
scriptInfo.ConnectionKey = bindingQueue.AddConnectionContext(connInfo);
service.ScriptParseInfoMap.Add(TestUri, scriptInfo);
service.ScriptParseInfoMap.TryAdd(TestUri, scriptInfo);
// When I call the language service
var fnResult = service.GetDefinition(fnDocument, scriptFile, connInfo);
@@ -807,7 +807,7 @@ GO";
Cleanup(fnResult.Locations);
Cleanup(sysResult.Locations);
Cleanup(masterResult.Locations);
service.ScriptParseInfoMap.Remove(TestUri);
service.ScriptParseInfoMap.TryRemove(TestUri, out _);
connInfo.RemoveAllConnections();
}

View File

@@ -218,7 +218,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
};
ParseResult parseResult = langService.ParseAndBind(scriptFile, null);
ParseResult parseResult = langService.ParseAndBind(scriptFile, null).GetAwaiter().GetResult();
ScriptParseInfo scriptParseInfo = langService.GetScriptParseInfo(scriptFile.ClientUri, true);
return new ScriptDocumentInfo(textDocumentPosition, scriptFile, scriptParseInfo);

View File

@@ -26,17 +26,17 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
public void LatestSqlParserIsUsedByDefault()
{
// This should only parse correctly on SQL server 2016 or newer
const string sql2016Text =
const string sql2016Text =
@"CREATE SECURITY POLICY [FederatedSecurityPolicy]" + "\r\n" +
@"ADD FILTER PREDICATE [rls].[fn_securitypredicate]([CustomerId])" + "\r\n" +
@"ADD FILTER PREDICATE [rls].[fn_securitypredicate]([CustomerId])" + "\r\n" +
@"ON [dbo].[Customer];";
LanguageService service = TestObjects.GetTestLanguageService();
// parse
var scriptFile = new ScriptFile();
scriptFile.SetFileContents(sql2016Text);
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile);
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile).GetAwaiter().GetResult();
// verify that no errors are detected
Assert.AreEqual(0, fileMarkers.Length);
@@ -57,7 +57,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
// parse the sql statement
var scriptFile = new ScriptFile();
scriptFile.SetFileContents(sqlWithErrors);
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile);
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile).GetAwaiter().GetResult();
// verify there are no errors
Assert.AreEqual(0, fileMarkers.Length);
@@ -78,7 +78,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
// parse sql statement
var scriptFile = new ScriptFile();
scriptFile.SetFileContents(sqlWithErrors);
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile);
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile).GetAwaiter().GetResult();
// verify there is one error
Assert.AreEqual(1, fileMarkers.Length);
@@ -97,7 +97,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
public void ParseMultilineSqlWithErrors()
{
// multiline sql with errors
const string sqlWithErrors =
const string sqlWithErrors =
"SELECT *** FROM sys.objects;\n" +
"GO\n" +
"SELECT *** FROM sys.objects;\n";
@@ -108,7 +108,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
// parse sql
var scriptFile = new ScriptFile();
scriptFile.SetFileContents(sqlWithErrors);
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile);
ScriptFileMarker[] fileMarkers = service.GetSemanticMarkers(scriptFile).GetAwaiter().GetResult();
// verify there are two errors
Assert.AreEqual(2, fileMarkers.Length);
@@ -140,29 +140,29 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
scriptFile.SetFileContents(docContent);
// When requesting SignatureHelp
SignatureHelp signatureHelp = service.GetSignatureHelp(TestObjects.GetTestDocPosition(), scriptFile);
SignatureHelp signatureHelp = service.GetSignatureHelp(TestObjects.GetTestDocPosition(), scriptFile).GetAwaiter().GetResult();
// Then null is returned as no parse info can be used to find the signature
Assert.Null(signatureHelp);
}
[Test]
public void EmptyCompletionListTest()
{
{
Assert.AreEqual(0, AutoCompleteHelper.EmptyCompletionList.Length);
}
internal sealed class TestScriptDocumentInfo : ScriptDocumentInfo
{
public TestScriptDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile, ScriptParseInfo scriptParseInfo,
public TestScriptDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile, ScriptParseInfo scriptParseInfo,
string tokenText = null)
:base(textDocumentPosition, scriptFile, scriptParseInfo)
: base(textDocumentPosition, scriptFile, scriptParseInfo)
{
this.tokenText = string.IsNullOrEmpty(tokenText) ? "doesntmatchanythingintheintellisensedefaultlist" : tokenText;
}
private string tokenText;
public override string TokenText
{
get
@@ -174,7 +174,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
[Test]
public void GetDefaultCompletionListWithNoMatchesTest()
{
{
var scriptFile = new ScriptFile();
scriptFile.SetFileContents("koko wants a bananas");
@@ -183,10 +183,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
var scriptDocumentInfo = new TestScriptDocumentInfo(
new TextDocumentPosition()
{
TextDocument = new TextDocumentIdentifier() { Uri = TestObjects.ScriptUri },
TextDocument = new TextDocumentIdentifier() { Uri = TestObjects.ScriptUri },
Position = new Position() { Line = 0, Character = 0 }
}, scriptFile, scriptInfo);
AutoCompleteHelper.GetDefaultCompletionItems(scriptDocumentInfo, false);
}