diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs index cd655179..c5f3ae3b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/LanguageService.cs @@ -847,6 +847,12 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri); + if (scriptParseInfo == null) + { + // Cache not set up yet - skip and wait until later + return null; + } + ConnectionInfo connInfo; LanguageService.ConnectionServiceInstance.TryFindConnection( scriptFile.ClientFilePath, @@ -858,7 +864,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices ParseAndBind(scriptFile, connInfo); } - if (scriptParseInfo != null && scriptParseInfo.ParseResult != null) + if (scriptParseInfo.ParseResult != null) { if (Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock)) { @@ -928,13 +934,14 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices // get the current script parse info object ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri); - ScriptDocumentInfo scriptDocumentInfo = new ScriptDocumentInfo(textDocumentPosition, scriptFile, scriptParseInfo); - + if (scriptParseInfo == null) { - return AutoCompleteHelper.GetDefaultCompletionItems(scriptDocumentInfo, useLowerCaseSuggestions); + return AutoCompleteHelper.GetDefaultCompletionItems(ScriptDocumentInfo.CreateDefaultDocumentInfo(textDocumentPosition, scriptFile), useLowerCaseSuggestions); } + ScriptDocumentInfo scriptDocumentInfo = new ScriptDocumentInfo(textDocumentPosition, scriptFile, scriptParseInfo); + // reparse and bind the SQL statement if needed if (RequiresReparse(scriptParseInfo, scriptFile)) { diff --git a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ScriptDocumentInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ScriptDocumentInfo.cs index b4120733..fd15ad82 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ScriptDocumentInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/LanguageServices/ScriptDocumentInfo.cs @@ -18,6 +18,16 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion /// Create new instance /// public ScriptDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile, ScriptParseInfo scriptParseInfo) + : this(textDocumentPosition, scriptFile) + { + Validate.IsNotNull(nameof(scriptParseInfo), scriptParseInfo); + + ScriptParseInfo = scriptParseInfo; + // need to adjust line & column for base-1 parser indices + Token = GetToken(scriptParseInfo, ParserLine, ParserColumn); + } + + private ScriptDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile) { StartLine = textDocumentPosition.Position.Line; ParserLine = textDocumentPosition.Position.Line + 1; @@ -30,11 +40,18 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion textDocumentPosition.Position.Line, textDocumentPosition.Position.Character); ParserColumn = textDocumentPosition.Position.Character + 1; - ScriptParseInfo = scriptParseInfo; Contents = scriptFile.Contents; + } - // need to adjust line & column for base-1 parser indices - Token = GetToken(scriptParseInfo, ParserLine, ParserColumn); + /// + /// Creates a new with no backing defined + /// + /// A + /// A to process + /// + public static ScriptDocumentInfo CreateDefaultDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile) + { + return new ScriptDocumentInfo(textDocumentPosition, scriptFile); } /// diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/LanguageServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/LanguageServiceTests.cs index c8c22986..a445fb49 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/LanguageServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.Test/LanguageServer/LanguageServiceTests.cs @@ -5,6 +5,7 @@ using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.LanguageServices; +using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts; using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts; using Microsoft.SqlTools.Test.Utility; using Xunit; @@ -125,6 +126,36 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServer Assert.Equal(10, fileMarkers[1].ScriptRegion.EndColumnNumber); Assert.Equal(3, fileMarkers[1].ScriptRegion.EndLineNumber); } + + [Fact] + public void GetSignatureHelpReturnsNullIfParseInfoNotInitialized() + { + // Given service doesn't have parseinfo intialized for a document + const string docContent = "SELECT * FROM sys.objects"; + LanguageService service = TestObjects.GetTestLanguageService(); + var scriptFile = new ScriptFile(); + scriptFile.SetFileContents(docContent); + + // When requesting SignatureHelp + SignatureHelp signatureHelp = service.GetSignatureHelp(CreateDummyDocPosition(), scriptFile); + + // Then null is returned as no parse info can be used to find the signature + Assert.Null(signatureHelp); + } + + private TextDocumentPosition CreateDummyDocPosition() + { + return new TextDocumentPosition + { + TextDocument = new TextDocumentIdentifier { Uri = TestObjects.ScriptUri }, + Position = new Position + { + Line = 0, + Character = 0 + } + }; + } + #endregion