/// <summary> /// Adds suggestions as appropriate to the internal Suggestions and SubstringSuggestions lists of intellisenseData. /// Returns true if intellisenseData is handled and no more suggestions are to be found and false otherwise. /// </summary> public bool Run(IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(intellisenseData); Contracts.AssertValue(intellisenseData.CurNode); if (intellisenseData.CurNode.Kind != NodeKind.StrLit && intellisenseData.CurNode.Kind != NodeKind.NumLit) { return(false); } TexlNode curNode = intellisenseData.CurNode; int cursorPos = intellisenseData.CursorPos; var tokenSpan = curNode.Token.Span; // Should not suggest anything if the cursor is before the string/number. if (cursorPos > tokenSpan.Lim && IntellisenseHelper.CanSuggestAfterValue(cursorPos, intellisenseData.Script)) { // Cursor is after the current node's token. // Suggest binary kewords. DType operandType = curNode.Kind == NodeKind.StrLit ? DType.String : DType.Number; IntellisenseHelper.AddSuggestionsForAfterValue(intellisenseData, operandType); } else if (cursorPos > tokenSpan.Min) { // Cursor is in the middle of the token, Suggest Globals matching the input string or number. int replacementLength = tokenSpan.Lim - tokenSpan.Min; intellisenseData.SetMatchArea(tokenSpan.Min, cursorPos, replacementLength); IntellisenseHelper.AddSuggestionsForGlobals(intellisenseData); } return(true); }
internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(intellisenseData); TexlNode curNode = intellisenseData.CurNode; int cursorPos = intellisenseData.CursorPos; BoolLitNode boolNode = curNode.CastBoolLit(); var tokenSpan = curNode.Token.Span; if (cursorPos < tokenSpan.Min) { // Cursor is before the token start. IntellisenseHelper.AddSuggestionsForValuePossibilities(intellisenseData, curNode); } else if (cursorPos <= tokenSpan.Lim) { // Cursor is in the middle of the token. int replacementLength = tokenSpan.Min == cursorPos ? 0 : tokenSpan.Lim - tokenSpan.Min; intellisenseData.SetMatchArea(tokenSpan.Min, cursorPos, replacementLength); intellisenseData.BoundTo = boolNode.Value ? TexlLexer.KeywordTrue : TexlLexer.KeywordFalse; IntellisenseHelper.AddSuggestionsForValuePossibilities(intellisenseData, curNode); } else if (IntellisenseHelper.CanSuggestAfterValue(cursorPos, intellisenseData.Script)) { // Verify that cursor is after a space after the current node's token. // Suggest binary keywords. IntellisenseHelper.AddSuggestionsForAfterValue(intellisenseData, DType.Boolean); } return(true); }
internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(intellisenseData); TexlNode curNode = intellisenseData.CurNode; int cursorPos = intellisenseData.CursorPos; // Cursor position is after the dot (If it was before the dot FindNode would have returned the left node). Contracts.Assert(curNode.Token.IsDottedNamePunctuator); Contracts.Assert(curNode.Token.Span.Lim <= cursorPos); DottedNameNode dottedNameNode = curNode.CastDottedName(); Identifier ident = dottedNameNode.Right; string identName = ident.Name; var leftNode = dottedNameNode.Left; DType leftType = intellisenseData.Binding.GetType(leftNode); intellisenseData.BeforeAddSuggestionsForDottedNameNode(leftNode); bool isOneColumnTable = leftType.IsColumn && leftNode.Kind == NodeKind.DottedName && leftType.Accepts(intellisenseData.Binding.GetType(((DottedNameNode)leftNode).Left)); if (cursorPos < ident.Token.Span.Min) { // Cursor position is before the identifier starts. // i.e. "this. | Awards" AddSuggestionsForLeftNodeScope(intellisenseData, leftNode, isOneColumnTable, leftType); } else if (cursorPos <= ident.Token.Span.Lim) { // Cursor position is in the identifier. // Suggest fields that don't need to be qualified. // Identifiers can include open and close brackets and the Token.Span covers them. // Get the matching string as a substring from the script so that the whitespace is preserved. intellisenseData.SetMatchArea(ident.Token.Span.Min, cursorPos, ident.Token.Span.Lim - ident.Token.Span.Min); if (!intellisenseData.Binding.ErrorContainer.HasErrors(dottedNameNode)) { intellisenseData.BoundTo = identName; } AddSuggestionsForLeftNodeScope(intellisenseData, leftNode, isOneColumnTable, leftType); } else if (IntellisenseHelper.CanSuggestAfterValue(cursorPos, intellisenseData.Script)) { // Verify that cursor is after a space after the identifier. // i.e. "this.Awards |" // Suggest binary operators. IntellisenseHelper.AddSuggestionsForAfterValue(intellisenseData, intellisenseData.Binding.GetType(dottedNameNode)); } return(true); }
internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(intellisenseData); TexlNode curNode = intellisenseData.CurNode; int cursorPos = intellisenseData.CursorPos; var tokenSpan = curNode.Token.Span; // Only suggest after record nodes if (cursorPos <= tokenSpan.Lim) { return(true); } if (IntellisenseHelper.CanSuggestAfterValue(cursorPos, intellisenseData.Script)) { // Verify that cursor is after a space after the current node's token. // Suggest binary keywords. IntellisenseHelper.AddSuggestionsForAfterValue(intellisenseData, DType.EmptyTable); } return(true); }
internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(intellisenseData); TexlNode curNode = intellisenseData.CurNode; int cursorPos = intellisenseData.CursorPos; FirstNameNode firstNameNode = curNode.CastFirstName(); Identifier ident = firstNameNode.Ident; int min = ident.Token.Span.Min; IdentToken tok = ident.Token; if (cursorPos < min) { // Cursor is before the beginning of the identifier or of the global token if present. // Suggest possibilities that can result in a value. IntellisenseHelper.AddSuggestionsForValuePossibilities(intellisenseData, curNode); intellisenseData.AddAdditionalSuggestionsForKeywordSymbols(curNode); } else if (cursorPos <= tok.Span.Lim) { // Cursor is part of the identifier or global token if present. // Get the matching string as a substring from the script so that the whitespace is preserved. IEnumerable <string> possibleFirstNames = intellisenseData.Binding.GetFirstNames().Select(firstNameInfo => firstNameInfo.Name.Value) .Union(intellisenseData.Binding.GetGlobalNames().Select(firstNameInfo => firstNameInfo.Name.Value)) .Union(intellisenseData.Binding.GetAliasNames().Select(firstNameInfo => firstNameInfo.Name.Value)) .Union(intellisenseData.SuggestableFirstNames); int replacementLength = IntellisenseHelper.GetReplacementLength(intellisenseData, tok.Span.Min, tok.Span.Lim, possibleFirstNames); intellisenseData.SetMatchArea(tok.Span.Min, cursorPos, replacementLength); intellisenseData.BoundTo = intellisenseData.Binding.ErrorContainer.HasErrors(firstNameNode) ? string.Empty : ident.Name; if (ident.AtToken != null || tok.Kind == TokKind.At) { // Suggest globals if cursor is after '@'. AddSuggestionsForScopedGlobals(intellisenseData); } else if (tok.HasDelimiters && cursorPos > tok.Span.Min) { // Suggest top level fields and globals if cursor is after a opening square bracket. IntellisenseHelper.AddSuggestionsForRuleScope(intellisenseData); IntellisenseHelper.AddSuggestionsForTopLevel(intellisenseData, curNode); IntellisenseHelper.AddSuggestionsForGlobals(intellisenseData); intellisenseData.AddAdditionalSuggestionsForLocalSymbols(); } else { // Suggest value posssibilities otherwise. IntellisenseHelper.AddSuggestionsForValuePossibilities(intellisenseData, curNode); } intellisenseData.AddAdditionalSuggestionsForKeywordSymbols(curNode); } else if (IsBracketOpen(tok.Span.Lim, cursorPos, intellisenseData.Script)) { AddSuggestionsForScopeFields(intellisenseData, intellisenseData.Binding.GetType(firstNameNode)); } else if (IntellisenseHelper.CanSuggestAfterValue(cursorPos, intellisenseData.Script)) { // Verify that cursor is after a space after the identifier. // Suggest binary keywords. IntellisenseHelper.AddSuggestionsForAfterValue(intellisenseData, intellisenseData.Binding.GetType(firstNameNode)); } return(true); }
internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(intellisenseData); TexlNode curNode = intellisenseData.CurNode; int cursorPos = intellisenseData.CursorPos; CallNode callNode = curNode.CastCall(); int spanMin = callNode.Head.Token.Span.Min; int spanLim = callNode.Head.Token.Span.Lim; // Handling the special case for service functions with non-empty namespaces. // We have to consider the namespace as the begining of the callNode for intellisense purposes. if (callNode.HeadNode != null) { var dottedNode = callNode.HeadNode.AsDottedName(); spanMin = dottedNode.Left.Token.Span.Min; spanLim = dottedNode.Right.Token.Span.Lim; } if (cursorPos < spanMin) { // Cursor is before the head // i.e. "| Filter(....)" // Suggest possibilities that can result in a value. IntellisenseHelper.AddSuggestionsForValuePossibilities(intellisenseData, callNode); } else if (cursorPos <= spanLim) { // Cursor is in the head. // Suggest function names. // Get the matching string as a substring from the script so that the whitespace is preserved. int replacementLength = IntellisenseHelper.GetReplacementLength(intellisenseData, spanMin, spanLim, intellisenseData.Binding.NameResolver.Functions.Select(function => function.Name)); // If we are replacing the full token, also include the opening paren (since this will be provided by the suggestion) if (replacementLength == spanLim - spanMin) { replacementLength += TexlLexer.PunctuatorParenOpen.Length; } intellisenseData.SetMatchArea(spanMin, cursorPos, replacementLength); intellisenseData.BoundTo = intellisenseData.Binding.ErrorContainer.HasErrors(callNode) ? string.Empty : callNode.Head.Name; IntellisenseHelper.AddSuggestionsForFunctions(intellisenseData); } else if (callNode.Token.Span.Lim > cursorPos || callNode.ParenClose == null) { // Handling the erroneous case when user enters a space after functionName and cursor is after space. // Cursor is before the open paren of the function. // Eg: "Filter | (" AND "Filter | (some Table, some predicate)" return(false); } else { // If there was no closed parenthesis we would have an error node. Contracts.Assert(callNode.ParenClose != null); if (cursorPos <= callNode.ParenClose.Span.Min) { // Cursor position is before the closed parenthesis and there are no arguments. // If there were arguments FindNode should have returned one of those. if (intellisenseData.CurFunc != null && intellisenseData.CurFunc.MaxArity > 0) { IntellisenseHelper.AddSuggestionsForTopLevel(intellisenseData, callNode); } } else if (IntellisenseHelper.CanSuggestAfterValue(cursorPos, intellisenseData.Script)) { // Verify that cursor is after a space after the closed parenthesis and // suggest binary operators. IntellisenseHelper.AddSuggestionsForAfterValue(intellisenseData, intellisenseData.Binding.GetType(callNode)); } } return(true); }