Beispiel #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);
            }
Beispiel #3
0
        public static void AddSuggestionsForUnaryOperatorKeyWords(IntellisenseData.IntellisenseData intellisenseData)
        {
            Contracts.AssertValue(intellisenseData);

            // TASK: 76039: Intellisense: Update intellisense to filter suggestions based on the expected type of the text being typed in UI
            IntellisenseHelper.AddSuggestionsForMatches(intellisenseData, TexlLexer.LocalizedInstance.GetUnaryOperatorKeywords(), SuggestionKind.KeyWord, SuggestionIconKind.Other, requiresSuggestionEscaping: false);
        }
            private void AddSuggestionsForLeftNodeScope(IntellisenseData.IntellisenseData intellisenseData, TexlNode leftNode, bool isOneColumnTable, DType leftType)
            {
                Contracts.AssertValue(intellisenseData);
                Contracts.AssertValue(leftNode);
                Contracts.AssertValue(leftType);

                if (!intellisenseData.TryAddSuggestionsForLeftNodeScope(leftNode))
                {
                    if (TryGetEnumInfo(intellisenseData, leftNode, intellisenseData.Binding, out EnumSymbol enumInfo))
                    {
                        IntellisenseHelper.AddSuggestionsForEnum(intellisenseData, enumInfo);
                    }
                    else if (TryGetNamespaceFunctions(leftNode, intellisenseData.Binding, out IEnumerable <TexlFunction> namespaceFunctions))
                    {
                        AddSuggestionsForNamespace(intellisenseData, namespaceFunctions);
                    }
                    else if (TryGetLocalScopeInfo(leftNode, intellisenseData.Binding, out ScopedNameLookupInfo info))
                    {
                        IntellisenseHelper.AddTopLevelSuggestions(intellisenseData, info.Type);
                    }
                    else if (!isOneColumnTable)
                    {
                        AddSuggestionsForDottedName(intellisenseData, leftType);
                    }
                }

                intellisenseData.OnAddedSuggestionsForLeftNodeScope(leftNode);
            }
 protected virtual UIString ConstructUIString(SuggestionKind suggestionKind, DType type, IntellisenseSuggestionList suggestions, string valueToSuggest, int highlightStart, int highlightEnd)
 {
     return(IntellisenseHelper.DisambiguateGlobals(suggestions,
                                                   new UIString(valueToSuggest, highlightStart, highlightEnd),
                                                   suggestionKind,
                                                   type));
 }
Beispiel #6
0
            internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData)
            {
                Contracts.AssertValue(intellisenseData);

                TexlNode curNode   = intellisenseData.CurNode;
                int      cursorPos = intellisenseData.CursorPos;
                // Cursor is in the operation token or before.
                // Suggest all value possibilities.
                UnaryOpNode unaryOpNode = curNode.CastUnaryOp();
                var         tokenSpan   = unaryOpNode.Token.Span;

                if (cursorPos < tokenSpan.Min)
                {
                    return(false);
                }

                Contracts.Assert(cursorPos >= tokenSpan.Min || cursorPos <= tokenSpan.Lim);

                string keyword = TexlParser.GetTokString(unaryOpNode.Token.Kind);

                Contracts.Assert(intellisenseData.MatchingLength <= keyword.Length);

                var replacementLength = tokenSpan.Min == cursorPos ? 0 : tokenSpan.Lim - tokenSpan.Min;

                intellisenseData.SetMatchArea(tokenSpan.Min, cursorPos, replacementLength);
                intellisenseData.BoundTo = intellisenseData.MatchingLength == 0 ? string.Empty : keyword;
                IntellisenseHelper.AddSuggestionsForValuePossibilities(intellisenseData, curNode);

                return(true);
            }
Beispiel #7
0
        public static bool FindCurFuncAndArgs(TexlNode curNode, int cursorPos, TexlBinding binding, out TexlFunction curFunc, out int argIndex, out int argCount, out DType expectedType)
        {
            Contracts.AssertValue(curNode);
            Contracts.AssertValue(binding);

            if (curNode.Kind == NodeKind.Call)
            {
                CallNode callNode = curNode.CastCall();
                if (callNode.Token.Span.Lim <= cursorPos && callNode.ParenClose != null && cursorPos <= callNode.ParenClose.Span.Min)
                {
                    CallInfo info = binding.GetInfo(callNode);

                    if (info.Function != null)
                    {
                        curFunc      = info.Function;
                        argIndex     = 0;
                        argCount     = callNode.Args.Count;
                        expectedType = curFunc.ParamTypes.Length > 0 ? curFunc.ParamTypes[0] : DType.Error;

                        return(true);
                    }
                }
            }

            if (IntellisenseHelper.TryGetInnerMostFunction(curNode, binding, out curFunc, out argIndex, out argCount))
            {
                expectedType = curFunc.ParamTypes.Length > argIndex ? curFunc.ParamTypes[argIndex] : DType.Error;
                return(true);
            }

            expectedType = DType.Error;
            return(false);
        }
            // Suggest the Globals that can appear in the context of '[@____]'
            // Suggesting controls, datasources, appVariables, and enums.
            private static void AddSuggestionsForScopedGlobals(IntellisenseData.IntellisenseData intellisenseData)
            {
                Contracts.AssertValue(intellisenseData);

                var suggestions = intellisenseData.AdditionalGlobalSuggestions
                                  .Union(intellisenseData.EnumSymbols.Select(symbol => new KeyValuePair <string, SuggestionIconKind>(symbol.Name, SuggestionIconKind.Other)));

                IntellisenseHelper.AddSuggestionsForMatches(intellisenseData, suggestions, SuggestionKind.Global, requiresSuggestionEscaping: true);
            }
            private static void AddSuggestionsForScopeFields(IntellisenseData.IntellisenseData intellisenseData, DType scope)
            {
                Contracts.AssertValue(intellisenseData);
                Contracts.Assert(scope.IsValid);

                foreach (var field in scope.GetNames(DPath.Root))
                {
                    IntellisenseHelper.AddSuggestion(intellisenseData, TexlLexer.PunctuatorAt + TexlLexer.EscapeName(field.Name.Value), SuggestionKind.Field, SuggestionIconKind.Other, field.Type, requiresSuggestionEscaping: false);
                }
            }
            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);
            }
            /// <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);

                if (intellisenseData.CurNode != null)
                {
                    return(false);
                }

                return(IntellisenseHelper.AddSuggestionsForValuePossibilities(intellisenseData, null));
            }
Beispiel #12
0
        public static void AddSuggestionsForEnums(IntellisenseData.IntellisenseData intellisenseData)
        {
            Contracts.AssertValue(intellisenseData);

            var suggestions          = intellisenseData.Suggestions;
            var substringSuggestions = intellisenseData.SubstringSuggestions;
            int countSuggBefore      = suggestions.Count;
            int countSubSuggBefore   = substringSuggestions.Count;

            foreach (var enumInfo in intellisenseData.EnumSymbols)
            {
                var enumType = enumInfo.EnumType;
                var enumName = enumInfo.Name;

                // TASK: 76039: Intellisense: Update intellisense to filter suggestions based on the expected type of the text being typed in UI
                IntellisenseHelper.AddSuggestion(intellisenseData, enumName, SuggestionKind.Enum, SuggestionIconKind.Other, enumType, requiresSuggestionEscaping: true);

                IntellisenseHelper.AddSuggestionsForEnum(intellisenseData, enumInfo, prefix: enumName + TexlLexer.PunctuatorDot);
            }

            if (suggestions.Count + substringSuggestions.Count == countSuggBefore + countSubSuggBefore + 1 && intellisenseData.SuggestUnqualifiedEnums)
            {
                string enumSuggestion = suggestions.Count > countSuggBefore ? suggestions[countSuggBefore].Text : substringSuggestions[countSubSuggBefore].Text;
                int    dotIndex       = enumSuggestion.LastIndexOf(TexlLexer.PunctuatorDot);

                // Assert '.' is not present or not at the beginning or the end of the EnumSuggestion
                Contracts.Assert(dotIndex == -1 || (0 < dotIndex && dotIndex < enumSuggestion.Length - 1));
                var unqualifiedEnum = enumSuggestion.Substring(dotIndex + 1);
                // If the Enum we are about suggest unqualified (i.e. just 'Blue' instead of Color!Blue)
                // has a name collision with some Item already in the suggestionlist we should not continue
                // and suggest it.
                if (suggestions.Any(x => x.Text == unqualifiedEnum) || substringSuggestions.Any(x => x.Text == unqualifiedEnum))
                {
                    return;
                }

                DType enumType;
                if (suggestions.Count > countSuggBefore)
                {
                    enumType = suggestions[countSuggBefore].Type;
                    suggestions.RemoveAt(suggestions.Count - 1);
                }
                else
                {
                    Contracts.Assert(substringSuggestions.Count > countSubSuggBefore);
                    enumType = substringSuggestions[countSubSuggBefore].Type;
                    substringSuggestions.RemoveAt(substringSuggestions.Count - 1);
                }

                // Add the unqualified Enum.
                // Note: The suggestion has already been escaped when it was previously added
                IntellisenseHelper.AddSuggestion(intellisenseData, unqualifiedEnum, SuggestionKind.Enum, SuggestionIconKind.Other, enumType, requiresSuggestionEscaping: false);
            }
        }
            internal void AddSuggestionsForNamespace(IntellisenseData.IntellisenseData intellisenseData, IEnumerable <TexlFunction> namespaceFunctions)
            {
                Contracts.AssertValue(intellisenseData);
                Contracts.AssertValue(namespaceFunctions);
                Contracts.AssertAllValues(namespaceFunctions);

                foreach (var function in namespaceFunctions)
                {
                    // Note we're using the unqualified name, since we're on the RHS of a "namespace." identifier.
                    IntellisenseHelper.AddSuggestion(intellisenseData, function.Name, SuggestionKind.Function, SuggestionIconKind.Function, function.ReturnType, requiresSuggestionEscaping: true);
                }
            }
Beispiel #14
0
        private static IEnumerable <KeyValuePair <string, DType> > StringTypeSuggestions(DType scopeType, int argumentIndex, out bool requiresSuggestionEscaping)
        {
            Contracts.AssertValid(scopeType);
            Contracts.Assert(0 <= argumentIndex);

            requiresSuggestionEscaping = true;

            if (argumentIndex == 0)
            {
                return(IntellisenseHelper.GetSuggestionsFromType(scopeType, DType.String));
            }

            return(EnumerableUtils.Yield <KeyValuePair <string, DType> >());
        }
Beispiel #15
0
        /// <summary>
        /// Adds suggestions that start with the matchingString from the top level scope of the binding.
        /// </summary>
        internal static void AddSuggestionsForRuleScope(IntellisenseData.IntellisenseData intellisenseData)
        {
            Contracts.AssertValue(intellisenseData);
            Contracts.AssertValue(intellisenseData.Binding);

            var scopeType = intellisenseData.ContextScope;

            if (scopeType == null)
            {
                return;
            }

            IntellisenseHelper.AddTopLevelSuggestions(intellisenseData, scopeType);
        }
            // This method has logic to create Types for the TypedNames for a given type
            // if that type is Table.
            internal static void AddSuggestionsForDottedName(IntellisenseData.IntellisenseData intellisenseData, DType type)
            {
                Contracts.AssertValue(intellisenseData);
                Contracts.AssertValid(type);

                if (intellisenseData.TryAddCustomDottedNameSuggestions(type))
                {
                    return;
                }

                if (!type.IsTable)
                {
                    IntellisenseHelper.AddTopLevelSuggestions(intellisenseData, type);
                    return;
                }

                IntellisenseHelper.AddSuggestionsForNamesInType(type, intellisenseData, createTableSuggestion: true);
            }
Beispiel #17
0
        internal static void AddSuggestionsForGlobals(IntellisenseData.IntellisenseData intellisenseData)
        {
            Contracts.AssertValue(intellisenseData);

            intellisenseData.AddCustomSuggestionsForGlobals();

            // Suggest function namespaces
            var namespaces = intellisenseData.Binding.NameResolver.Functions.Select(func => func.Namespace).Distinct();

            foreach (var funcNamespace in namespaces)
            {
                if (funcNamespace == DPath.Root)
                {
                    continue;
                }

                IntellisenseHelper.AddSuggestion(intellisenseData, funcNamespace.Name, SuggestionKind.Global, SuggestionIconKind.Other, DType.Unknown, requiresSuggestionEscaping: true);
            }
        }
Beispiel #18
0
        internal static bool AddSuggestionsForValuePossibilities(IntellisenseData.IntellisenseData intellisenseData, TexlNode node)
        {
            Contracts.AssertValue(intellisenseData);
            Contracts.AssertValue(node);

            int suggestionCount = intellisenseData.Suggestions.Count() + intellisenseData.SubstringSuggestions.Count();

            IntellisenseHelper.AddSuggestionsForRuleScope(intellisenseData);
            IntellisenseHelper.AddSuggestionsForTopLevel(intellisenseData, node);
            IntellisenseHelper.AddSuggestionsForFunctions(intellisenseData);
            intellisenseData.AddSuggestionsForConstantKeywords();
            IntellisenseHelper.AddSuggestionsForGlobals(intellisenseData);
            intellisenseData.AfterAddSuggestionsForGlobals();
            IntellisenseHelper.AddSuggestionsForUnaryOperatorKeyWords(intellisenseData);
            intellisenseData.AfterAddSuggestionsForUnaryOperatorKeywords();
            IntellisenseHelper.AddSuggestionsForEnums(intellisenseData);

            intellisenseData.AddCustomSuggestionsForValuePossibilities();

            return(suggestionCount < (intellisenseData.Suggestions.Count() + intellisenseData.SubstringSuggestions.Count()));
        }
            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);
            }
Beispiel #20
0
            internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData)
            {
                Contracts.AssertValue(intellisenseData);

                // For Error Kind, suggest top level values only in the context of a callNode and
                // ThisItemProperties only in the context of thisItem.
                TexlNode curNode = intellisenseData.CurNode;

                // Three methods that implement custom behavior here, one that adds suggestions before
                // top level suggestions are added, one after, and one to handle the case where there aren't
                // any top level suggestions to add.
                if (intellisenseData.AddSuggestionsBeforeTopLevelErrorNodeSuggestions())
                {
                    return(true);
                }

                if (!IntellisenseHelper.AddSuggestionsForTopLevel(intellisenseData, curNode))
                {
                    intellisenseData.AddAlternativeTopLevelSuggestionsForErrorNode();
                }

                intellisenseData.AddSuggestionsAfterTopLevelErrorNodeSuggestions();
                return(true);
            }
Beispiel #21
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);

                if (!TryGetRecordNodeWithinACallNode(intellisenseData.CurNode, out RecordNode recordNode, out CallNode callNode))
                {
                    return(false);
                }

                // For the special case of an identifier of a record which is an argument of a function, we can
                // utilize the data provided to suggest relevant column names
                int cursorPos = intellisenseData.CursorPos;

                bool suggestionsAdded = false;

                Contracts.AssertValue(recordNode);
                Contracts.AssertValue(callNode);

                Identifier columnName = GetRecordIdentifierForCursorPosition(cursorPos, recordNode, intellisenseData.Script);

                if (columnName == null)
                {
                    return(false);
                }

                if (columnName.Token.Span.Min <= cursorPos)
                {
                    var tokenSpan         = columnName.Token.Span;
                    int replacementLength = tokenSpan.Min == cursorPos ? 0 : tokenSpan.Lim - tokenSpan.Min;
                    intellisenseData.SetMatchArea(tokenSpan.Min, cursorPos, replacementLength);
                }

                CallInfo info = intellisenseData.Binding.GetInfo(callNode);
                var      func = info.Function;

                if (func == null || !intellisenseData.IsFunctionElligibleForRecordSuggestions(func))
                {
                    return(false);
                }

                // Adding suggestions for callNode arguments which reference a collection's columns
                if (func.CanSuggestInputColumns)
                {
                    DType aggregateType = GetAggregateType(func, callNode, intellisenseData);
                    if (aggregateType.HasErrors || !aggregateType.IsAggregate)
                    {
                        return(false);
                    }

                    if (aggregateType.ContainsDataEntityType(DPath.Root))
                    {
                        bool error = false;
                        aggregateType = aggregateType.DropAllOfTableRelationships(ref error, DPath.Root);
                        if (error)
                        {
                            return(false);
                        }
                    }

                    foreach (TypedName tName in aggregateType.GetNames(DPath.Root))
                    {
                        var    usedName = tName.Name;
                        string maybeDisplayName;
                        if (DType.TryGetDisplayNameForColumn(aggregateType, usedName, out maybeDisplayName))
                        {
                            usedName = new DName(maybeDisplayName);
                        }

                        string suggestion = TexlLexer.EscapeName(usedName.Value) + (IntellisenseHelper.IsPunctuatorColonNextToCursor(cursorPos, intellisenseData.Script) ? "" : TexlLexer.PunctuatorColon);
                        suggestionsAdded |= IntellisenseHelper.AddSuggestion(intellisenseData, suggestion, SuggestionKind.Field, SuggestionIconKind.Other, DType.String, requiresSuggestionEscaping: false);
                    }

                    return(suggestionsAdded && columnName != null);
                }

                return(intellisenseData.TryAddFunctionRecordSuggestions(func, callNode, columnName));
            }
            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);
            }
 protected virtual bool CheckAndAddSuggestion(IntellisenseSuggestionList suggestions, IntellisenseSuggestion candidate)
 {
     return(IntellisenseHelper.CheckAndAddSuggestion(candidate, suggestions));
 }
        public bool AddSuggestion(IntellisenseData.IntellisenseData intellisenseData, string suggestion, SuggestionKind suggestionKind, SuggestionIconKind iconKind, DType type, bool requiresSuggestionEscaping, uint sortPriority = 0)
        {
            Contracts.AssertValue(intellisenseData);
            Contracts.AssertValue(suggestion);

            if (!intellisenseData.DetermineSuggestibility(suggestion, type))
            {
                return(false);
            }

            IntellisenseSuggestionList suggestions          = intellisenseData.Suggestions;
            IntellisenseSuggestionList substringSuggestions = intellisenseData.SubstringSuggestions;
            int    matchingLength = intellisenseData.MatchingLength;
            string matchingStr    = intellisenseData.MatchingStr;
            string boundTo        = intellisenseData.BoundTo;

            var valueToSuggest = requiresSuggestionEscaping ? TexlLexer.EscapeName(suggestion) : suggestion;
            int highlightStart = suggestion.IndexOf(matchingStr, StringComparison.OrdinalIgnoreCase);

            // If the suggestion has special characters we need to find the highlightStart index by escaping the matching string as well.
            // Because, the suggestion could be something like 'Ident with Space' and the user might have typed Ident. In this case,
            // we want to highlight only Ident while displaying 'Ident with Space'.
            if (requiresSuggestionEscaping && !string.IsNullOrEmpty(matchingStr) && valueToSuggest != suggestion && highlightStart == 0)
            {
                highlightStart++;
            }
            else
            {
                matchingLength--;
            }

            int highlightEnd = highlightStart + matchingStr.Length;

            if (IntellisenseHelper.IsMatch(suggestion, matchingStr))
            {
                // In special circumstance where the user escapes an identifier where they don't have to, the matching length will
                // include the starting delimiter that user provided, where as the suggestion would not include any delimiters.
                // Hence we have to count for that fact.
                if (matchingLength > 0 & matchingLength > matchingStr.Length)
                {
                    highlightEnd = matchingLength > valueToSuggest.Length ? valueToSuggest.Length : matchingLength;
                }
                UIString UIsuggestion            = ConstructUIString(suggestionKind, type, suggestions, valueToSuggest, highlightStart, highlightEnd);
                IntellisenseSuggestion candidate = new IntellisenseSuggestion(UIsuggestion,
                                                                              suggestionKind,
                                                                              iconKind,
                                                                              type,
                                                                              boundTo,
                                                                              -1,
                                                                              string.Empty,
                                                                              null,
                                                                              sortPriority);
                return(CheckAndAddSuggestion(suggestions, candidate));
            }
            if (highlightStart > -1)
            {
                UIString UIsuggestion            = ConstructUIString(suggestionKind, type, substringSuggestions, valueToSuggest, highlightStart, highlightEnd);
                IntellisenseSuggestion candidate = new IntellisenseSuggestion(UIsuggestion,
                                                                              suggestionKind,
                                                                              iconKind,
                                                                              type,
                                                                              boundTo,
                                                                              -1,
                                                                              string.Empty,
                                                                              null,
                                                                              sortPriority);

                return(CheckAndAddSuggestion(substringSuggestions, candidate));
            }

            return(false);
        }
            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);
            }