// // 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 Microsoft.SqlServer.Management.SqlParser.Parser; using Microsoft.SqlTools.Utility; using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts; using System.Collections.Generic; namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion { /// /// A class to calculate the numbers used by SQL parser using the text positions and content /// public class ScriptDocumentInfo { /// /// 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; StartColumn = TextUtilities.PositionOfPrevDelimeter( scriptFile.Contents, textDocumentPosition.Position.Line, textDocumentPosition.Position.Character); EndColumn = TextUtilities.PositionOfNextDelimeter( scriptFile.Contents, textDocumentPosition.Position.Line, textDocumentPosition.Position.Character); ParserColumn = textDocumentPosition.Position.Character + 1; Contents = scriptFile.Contents; } /// /// Creates a new with no backing defined /// /// A /// A to process /// public static ScriptDocumentInfo CreateDefaultDocumentInfo(TextDocumentPosition textDocumentPosition, ScriptFile scriptFile) { return new ScriptDocumentInfo(textDocumentPosition, scriptFile); } /// /// Gets a string containing the full contents of the file. /// public string Contents { get; private set; } /// /// Script Parse Info Instance /// public ScriptParseInfo ScriptParseInfo { get; private set; } /// /// Start Line /// public int StartLine { get; private set; } /// /// Parser Line /// public int ParserLine { get; private set; } /// /// Start Column /// public int StartColumn { get; private set; } /// /// end Column /// public int EndColumn { get; private set; } /// /// Parser Column /// public int ParserColumn { get; private set; } /// /// The token text in the file content used for completion list /// public virtual string TokenText { get { return Token != null ? Token.Text : null; } } /// /// The token in the file content used for completion list /// public Token Token { get; private set; } /// /// Returns the token that will be used by SQL parser for creating the completion list /// internal 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 the current token int currentIndex = 0; foreach (var token in scriptParseInfo.ParseResult.Script.Tokens) { if (currentIndex == tokenIndex) { return token; } ++currentIndex; } } } return null; } /// /// Returns the token that is used for Peek Definition objects /// internal static Tuple, Queue> GetPeekDefinitionTokens(ScriptParseInfo scriptParseInfo, int startLine, int startColumn) { Stack childrenTokens = new Stack(); Queue parentTokens = new Queue(); 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 the current token and the ones to its right // until we hit a whitespace token int currentIndex = 0; foreach (var token in scriptParseInfo.ParseResult.Script.Tokens) { if (currentIndex == tokenIndex) { // push all parent tokens until we hit whitespace int parentIndex = currentIndex; while (true) { if (scriptParseInfo.ParseResult.Script.TokenManager.GetToken(parentIndex).Type != "LEX_WHITE") { parentTokens.Enqueue(scriptParseInfo.ParseResult.Script.TokenManager.GetToken(parentIndex)); parentIndex--; } else { break; } } } else if (currentIndex > tokenIndex) { // push all children tokens until we hit whitespace if (scriptParseInfo.ParseResult.Script.TokenManager.GetToken(currentIndex).Type != "LEX_WHITE") { childrenTokens.Push(token); } else { break; } } ++currentIndex; } return Tuple.Create(childrenTokens, parentTokens); } } return null; } } }