Exemplo n.º 1
0
            /// <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);
            }