/// <summary> /// Adds suggestions for an enum, with an optional prefix. /// </summary> /// <param name="intellisenseData"></param> /// <param name="enumInfo"></param> /// <param name="prefix"></param> internal static void AddSuggestionsForEnum(IntellisenseData.IntellisenseData intellisenseData, EnumSymbol enumInfo, string prefix = "") { Contracts.AssertValue(intellisenseData); Contracts.AssertValue(enumInfo); Contracts.AssertValue(prefix); bool anyCollisionExists = false; var locNameTypePairs = new List <Tuple <String, DType> >(); // We do not need to get the localized names since GetNames will only return invariant. // Instead, we use the invariant names later with the enumInfo to retrieve the localized name. foreach (var typedName in enumInfo.EnumType.GetNames(DPath.Root)) { string locName; enumInfo.TryGetLocValueName(typedName.Name.Value, out locName).Verify(); string escapedLocName = TexlLexer.EscapeName(locName); var collisionExists = intellisenseData.DoesNameCollide(locName); if (collisionExists) { string candidate = prefix + escapedLocName; bool canAddSuggestion = _addSuggestionDryRunHelper.AddSuggestion(intellisenseData, candidate, SuggestionKind.Global, SuggestionIconKind.Other, typedName.Type, false); anyCollisionExists = anyCollisionExists || canAddSuggestion; } locNameTypePairs.Add(new Tuple <String, DType>(escapedLocName, typedName.Type)); } foreach (var locNameTypePair in locNameTypePairs) { string suggestion = anyCollisionExists || !intellisenseData.SuggestUnqualifiedEnums ? prefix + locNameTypePair.Item1 : locNameTypePair.Item1; AddSuggestion(intellisenseData, suggestion, SuggestionKind.Global, SuggestionIconKind.Other, locNameTypePair.Item2, false); } }
// Convert this DPath to a string in dotted syntax, such as "screen1.group6.label3" public string ToDottedSyntax(string punctuator = ".", bool escapeInnerName = false) { Contracts.AssertNonEmpty(punctuator); Contracts.Assert(punctuator.Length == 1); Contracts.Assert(".!".IndexOf(punctuator[0]) >= 0); if (IsRoot) { return(string.Empty); } Contracts.Assert(Length > 0); int count = 0; for (Node node = _node; node != null; node = node.Parent) { count += node.Name.Value.Length; } StringBuilder sb = new StringBuilder(count + Length - 1); string sep = string.Empty; for (int i = 0; i < Length; i++) { sb.Append(sep); var escapedName = escapeInnerName ? TexlLexer.EscapeName(this[i]) : this[i]; sb.Append(escapedName); sep = punctuator; } return(sb.ToString()); }
private static IEnumerable <KeyValuePair <string, DType> > TimeUnitSuggestions(TryGetEnumSymbol tryGetEnumSymbol, bool suggestUnqualifedEnums, DType scopeType, int argumentIndex, out bool requiresSuggestionEscaping) { Contracts.Assert(scopeType.IsValid); Contracts.Assert(2 == argumentIndex); requiresSuggestionEscaping = false; var retVal = new List <KeyValuePair <string, DType> >(); if (argumentIndex == 2 && tryGetEnumSymbol(EnumConstants.TimeUnitEnumString, out var enumInfo)) { Contracts.AssertValue(enumInfo); foreach (var name in enumInfo.EnumType.GetNames(DPath.Root)) { string locName; enumInfo.TryGetLocValueName(name.Name.Value, out locName).Verify(); if (suggestUnqualifedEnums) { retVal.Add(new KeyValuePair <string, DType>(TexlLexer.EscapeName(locName), name.Type)); } else { retVal.Add(new KeyValuePair <string, DType>(TexlLexer.EscapeName(enumInfo.Name) + TexlLexer.PunctuatorDot + TexlLexer.EscapeName(locName), name.Type)); } } } return(retVal); }
public LanguageSettings(string cultureName, string uiCultureName, bool addPunctuators = false) { Contracts.AssertNonEmpty(cultureName); _cultureName = cultureName; _uiCultureName = uiCultureName; _locToInvariantFunctionMap = new Dictionary <string, string>(); _locToInvariantPunctuatorMap = new Dictionary <string, string>(); _invariantToLocFunctionMap = new Dictionary <string, string>(); _invariantToLocPunctuatorMap = new Dictionary <string, string>(); _cacheStamp = 0; _cachedInvariantSettings = null; if (addPunctuators) { string dec; string comma; string list; TexlLexer.ChoosePunctuators(this, out dec, out comma, out list); AddPunctuator(dec, TexlLexer.PunctuatorDecimalSeparatorInvariant); AddPunctuator(comma, TexlLexer.PunctuatorCommaInvariant); AddPunctuator(list, TexlLexer.PunctuatorSemicolonInvariant); } }
// Parses a typed name map specification, returns true and sets 'map' on success. // A map specification has the form: [name:type, ...] private static bool TryParseTypeMap(DTypeSpecLexer lexer, out TypeTree map) { Contracts.AssertValue(lexer); string token; if (!lexer.TryNextToken(out token) || token != "[") { map = default(TypeTree); return(false); } map = new TypeTree(); while (lexer.TryNextToken(out token) && token != "]") { string name = token; if (name.Length >= 2 && name.StartsWith("'") && name.EndsWith("'")) { name = TexlLexer.UnescapeName(name); } DType type; if (!DName.IsValidDName(name) || !lexer.TryNextToken(out token) || token != ":" || map.Contains(name) || !TryParse(lexer, out type)) { map = default(TypeTree); return(false); } map = map.SetItem(name, type); if (!lexer.TryNextToken(out token) || (token != "," && token != "]")) { map = default(TypeTree); return(false); } else if (token == "]") { return(true); } } if (token != "]") { map = default(TypeTree); return(false); } return(true); }
private static Token[] TokenizeScript(string script, ILanguageSettings loc = null, Flags flags = Flags.None) { Contracts.AssertValue(script); Contracts.AssertValueOrNull(loc); TexlLexer.Flags lexerFlags = flags.HasFlag(Flags.AllowReplaceableExpressions) ? TexlLexer.Flags.AllowReplaceableTokens : TexlLexer.Flags.None; if (loc == null) { return(TexlLexer.LocalizedInstance.LexSource(script, lexerFlags)); } return(TexlLexer.NewInstance(loc).LexSource(script, lexerFlags)); }
public void TestIsIdentStart() { Tuple <char, char>[] identStartRanges = new Tuple <char, char>[] { Tuple.Create('A', 'Z'), Tuple.Create('a', 'z'), Tuple.Create('_', '_'), Tuple.Create('\'', '\''), }; for (int i = 0; i < 128; i++) { char ch = (char)i; Assert.Equal(LexerTest.IsInRangeInclusive(ch, identStartRanges), TexlLexer.IsIdentStart(ch)); } }
public void TestTryNameOrIdentifierToName() { DName name; Assert.True(TexlLexer.TryNameOrIdentifierToName(" Name ", out name)); Assert.Equal("Name", name); Assert.True(TexlLexer.TryNameOrIdentifierToName(" Name Abcd ", out name)); Assert.Equal("Name Abcd", name); Assert.True(TexlLexer.TryNameOrIdentifierToName(" Name Abcd", out name)); Assert.Equal("Name Abcd", name); Assert.True(TexlLexer.TryNameOrIdentifierToName("Name Abcd", out name)); Assert.Equal("Name Abcd", name); Assert.True(TexlLexer.TryNameOrIdentifierToName("Name\bAbcd", out name)); Assert.Equal("Name\bAbcd", name); Assert.False(TexlLexer.TryNameOrIdentifierToName(string.Empty, out name)); Assert.False(TexlLexer.TryNameOrIdentifierToName(" ", out name)); Assert.True(TexlLexer.TryNameOrIdentifierToName(" 'Escaped Name'", out name)); Assert.Equal("Escaped Name", name); Assert.True(TexlLexer.TryNameOrIdentifierToName(" 'Escaped '' Name'", out name)); Assert.Equal("Escaped ' Name", name); Assert.True(TexlLexer.TryNameOrIdentifierToName(" 'Escaped '' Name'''", out name)); Assert.Equal("Escaped ' Name'", name); Assert.False(TexlLexer.TryNameOrIdentifierToName(" ' ' ", out name)); Assert.True(TexlLexer.TryNameOrIdentifierToName(" ''''", out name)); Assert.Equal("'", name); Assert.True(TexlLexer.TryNameOrIdentifierToName(" 'Escaped Name' ", out name)); Assert.Equal("Escaped Name", name); Assert.False(TexlLexer.TryNameOrIdentifierToName(" 'Escaped Name ", out name)); Assert.False(TexlLexer.TryNameOrIdentifierToName(" 'Escaped Name ' abc ", out name)); Assert.True(TexlLexer.TryNameOrIdentifierToName(" '#a!>>?'", out name)); Assert.Equal("#a!>>?", name); Assert.True(TexlLexer.TryNameOrIdentifierToName(" '123\u00ae'", out name)); Assert.Equal("123\u00ae", name); }
public void TestLexNumbersWithLanguageSettings() { Token[] tokens; tokens = TexlLexer.NewInstance(SomeFrenchLikeSettings()).LexSource("123456,78"); Assert.NotNull(tokens); Assert.Equal(2, tokens.Length); Assert.Equal(TokKind.NumLit, tokens[0].Kind); Assert.Equal(123456.78, tokens[0].As <NumLitToken>().Value); tokens = TexlLexer.NewInstance(SomeRomanianLikeSettings()).LexSource("10`12345"); Assert.NotNull(tokens); Assert.Equal(2, tokens.Length); Assert.Equal(TokKind.NumLit, tokens[0].Kind); Assert.Equal(10.12345, tokens[0].As <NumLitToken>().Value); }
// This method returns the suggestions for latter arguments of the If function based on the second argument (the true result) private static IEnumerable <KeyValuePair <string, DType> > IfSuggestions(TryGetEnumSymbol tryGetEnumSymbol, bool suggestUnqualifedEnums, DType scopeType, int argumentIndex, out bool requiresSuggestionEscaping) { Contracts.Assert(scopeType.IsValid); Contracts.Assert(0 <= argumentIndex); requiresSuggestionEscaping = false; if (argumentIndex <= 1) { return(EnumerableUtils.Yield <KeyValuePair <string, DType> >()); } return(scopeType .GetNames(DPath.Root) .Select(name => new KeyValuePair <string, DType>(TexlLexer.EscapeName(name.Name.Value), name.Type))); }
public void TestLexListsWithLanguageSettings() { Token[] tokens; tokens = TexlLexer.NewInstance(SomeFrenchLikeSettings()).LexSource("[1,2;2,3;4]"); Assert.NotNull(tokens); Assert.Equal(8, tokens.Length); Assert.Equal(TokKind.BracketOpen, tokens[0].Kind); Assert.Equal(TokKind.NumLit, tokens[1].Kind); Assert.Equal(1.2, tokens[1].As <NumLitToken>().Value); Assert.Equal(TokKind.Comma, tokens[2].Kind); Assert.Equal(TokKind.NumLit, tokens[3].Kind); Assert.Equal(2.3, tokens[3].As <NumLitToken>().Value); Assert.Equal(TokKind.Comma, tokens[4].Kind); Assert.Equal(TokKind.NumLit, tokens[5].Kind); Assert.Equal(4, tokens[5].As <NumLitToken>().Value); Assert.Equal(TokKind.BracketClose, tokens[6].Kind); Assert.Equal(TokKind.Eof, tokens[7].Kind); }
/// <summary> /// Adds suggestions that start with the MatchingString from the given type. /// </summary> internal static void AddTopLevelSuggestions(IntellisenseData.IntellisenseData intellisenseData, DType type, string prefix = "") { Contracts.AssertValue(intellisenseData); Contracts.Assert(type.IsValid); Contracts.Assert(prefix.Length == 0 || (TexlLexer.PunctuatorBang + TexlLexer.PunctuatorDot).IndexOf(prefix[prefix.Length - 1]) >= 0); foreach (TypedName tName in type.GetAllNames(DPath.Root)) { if (!intellisenseData.TryAddCustomColumnTypeSuggestions(tName.Type)) { var usedName = tName.Name; string maybeDisplayName; if (DType.TryGetDisplayNameForColumn(type, usedName, out maybeDisplayName)) { usedName = new DName(maybeDisplayName); } AddSuggestion(intellisenseData, prefix + TexlLexer.EscapeName(usedName.Value), SuggestionKind.Global, SuggestionIconKind.Other, tName.Type, requiresSuggestionEscaping: false); } } }
public void TestLexSemicolonListsWithLanguageSettings() { Token[] tokens; tokens = TexlLexer.NewInstance(SomeFrenchLikeSettings()).LexSource("A ;; B ;; C"); Assert.NotNull(tokens); Assert.Equal(10, tokens.Length); Assert.Equal(TokKind.Ident, tokens[0].Kind); Assert.Equal("A", tokens[0].As <IdentToken>().Name.Value); Assert.Equal(TokKind.Whitespace, tokens[1].Kind); Assert.Equal(TokKind.Semicolon, tokens[2].Kind); Assert.Equal(TokKind.Whitespace, tokens[3].Kind); Assert.Equal(TokKind.Ident, tokens[4].Kind); Assert.Equal("B", tokens[4].As <IdentToken>().Name.Value); Assert.Equal(TokKind.Whitespace, tokens[5].Kind); Assert.Equal(TokKind.Semicolon, tokens[6].Kind); Assert.Equal(TokKind.Whitespace, tokens[7].Kind); Assert.Equal(TokKind.Ident, tokens[8].Kind); Assert.Equal("C", tokens[8].As <IdentToken>().Name.Value); Assert.Equal(TokKind.Eof, tokens[9].Kind); }
/// <summary> /// This method returns the suggestions for second and third arguments of the Text function. /// </summary> /// <param name="tryGetEnumSymbol"> /// Getter for enum symbols intended for the suggestions /// </param> /// <param name="suggestUnescapedEnums"> /// Whether to suggest unescaped enums /// </param> /// <param name="scopeType"> /// Type of the enclosing scope from where intellisense is run /// </param> /// <param name="argumentIndex"> /// The current index of the argument from where intellisense is run /// </param> /// <param name="requiresSuggestionEscaping"> /// Set to whether the argument needs to be string escaped /// </param> /// <returns> /// Enumerable of suggestions wherein the key is the suggestion text and the value is its type /// </returns> private static IEnumerable <KeyValuePair <string, DType> > TextSuggestions(TryGetEnumSymbol tryGetEnumSymbol, bool suggestUnescapedEnums, DType scopeType, int argumentIndex, out bool requiresSuggestionEscaping) { Contracts.Assert(scopeType.IsValid); Contracts.Assert(0 <= argumentIndex); requiresSuggestionEscaping = true; if (argumentIndex != 1 && argumentIndex != 2) { return(EnumerableUtils.Yield <KeyValuePair <string, DType> >()); } if (argumentIndex == 1) { if (!DType.DateTime.Accepts(scopeType) || !tryGetEnumSymbol(EnumConstants.DateTimeFormatEnumString, out var enumInfo)) { return(EnumerableUtils.Yield <KeyValuePair <string, DType> >()); } var retVal = new List <KeyValuePair <string, DType> >(); Contracts.AssertValue(enumInfo); requiresSuggestionEscaping = false; foreach (var name in enumInfo.EnumType.GetNames(DPath.Root)) { string locName; enumInfo.TryGetLocValueName(name.Name.Value, out locName).Verify(); retVal.Add(new KeyValuePair <string, DType>(TexlLexer.EscapeName(enumInfo.Name) + TexlLexer.PunctuatorDot + TexlLexer.EscapeName(locName), name.Type)); } return(retVal); } else { Contracts.Assert(argumentIndex == 2); requiresSuggestionEscaping = false; return(GetLanguageCodeSuggestions()); } }
internal bool SetMatchArea(int startIndex, int endIndex, int replacementLength = -1) { Contracts.Assert(0 <= startIndex && startIndex <= endIndex && endIndex <= _script.Length); // If we have already provided suggestions, we can't set the match area if (Suggestions.Count > 0 || SubstringSuggestions.Count > 0) { return(false); } // Trim leading whitespace as there is no point to matching it while (startIndex < endIndex && string.IsNullOrWhiteSpace(Script.Substring(startIndex, 1))) { startIndex++; } _replacementStartIndex = startIndex; _matchingLength = endIndex - startIndex; _replacementLength = replacementLength < 0 ? _matchingLength : replacementLength; _matchingStr = TexlLexer.UnescapeName(_script.Substring(startIndex, _matchingLength)); return(true); }
public void TestUnsupportedDecimalSeparatorCausesFallback() { Token[] tokens; // Simulate an override of the decimal separator to something that AXL does not support. var oldCulture = CultureInfo.CurrentCulture; var newCulture = new CultureInfo(CultureInfo.CurrentCulture.Name); newCulture.NumberFormat.NumberDecimalSeparator = "+"; CultureInfo.CurrentCulture = newCulture; // The lexer should fall back to the invariant separator. TexlLexer lexer = TexlLexer.NewInstance(null); Assert.Equal(lexer.LocalizedPunctuatorDecimalSeparator, TexlLexer.PunctuatorDecimalSeparatorInvariant); tokens = lexer.LexSource("123456.78"); Assert.NotNull(tokens); Assert.Equal(2, tokens.Length); Assert.Equal(TokKind.NumLit, tokens[0].Kind); Assert.Equal(123456.78, tokens[0].As <NumLitToken>().Value); CultureInfo.CurrentCulture = oldCulture; }
/// <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)); }
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); } }
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); }