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