static TriggerState GetTriggerState(string expression, out int triggerLength, out ExpressionNode triggerExpression) { triggerLength = 0; if (expression.Length == 0) { triggerExpression = new ExpressionText(0, "", true); return(TriggerState.Value); } if (expression.Length == 1) { triggerExpression = new ExpressionText(0, expression, true); triggerLength = 1; return(TriggerState.Value); } const ExpressionOptions options = ExpressionOptions.ItemsMetadataAndLists | ExpressionOptions.CommaLists; triggerExpression = ExpressionParser.Parse(expression, options); if (triggerExpression is ExpressionList el) { //the last list entry is the thing that triggered it triggerExpression = el.Nodes.Last(); if (triggerExpression is ExpressionError e && e.Kind == ExpressionErrorKind.EmptyListEntry) { return(LastChar() == ',' ? TriggerState.CommaValue : TriggerState.SemicolonValue); } if (triggerExpression is ExpressionText l) { if (l.Length == 1) { triggerLength = 1; return(PenultimateChar() == ',' ? TriggerState.CommaValue : TriggerState.SemicolonValue); } } } //find the deepest node that touches the end var lastNode = triggerExpression.Find(expression.Length); if (lastNode == null) { return(TriggerState.None); } if (lastNode is ExpressionText lit) { if (LastChar() == '\\') { return(TriggerState.DirectorySeparator); } if (lit.Value.Length >= 2 && PenultimateChar() == '\\' && IsPossiblePathSegment(LastChar())) { triggerLength = 1; return(TriggerState.DirectorySeparator); } } //find the deepest error var error = lastNode as ExpressionError; ExpressionNode parent = lastNode.Parent; while (parent != null && error == null) { error = parent as ExpressionError; parent = parent.Parent; } if (error is IncompleteExpressionError iee && iee.WasEOF) { switch (lastNode) { case ExpressionItem i: if (iee.Kind == ExpressionErrorKind.ExpectingMethodOrTransform) { return(TriggerState.ItemFunctionName); } break; case ExpressionItemName ein: if (iee.Kind == ExpressionErrorKind.ExpectingRightParenOrDash) { triggerLength = ein.Name.Length; return(TriggerState.Item); } break; case ExpressionPropertyName pn: if (iee.Kind == ExpressionErrorKind.ExpectingRightParenOrPeriod) { triggerLength = pn.Name.Length; return(TriggerState.Property); } break; case ExpressionFunctionName fn: if (iee.Kind == ExpressionErrorKind.IncompleteProperty) { triggerLength = fn.Name.Length; return(TriggerState.PropertyFunctionName); } if (iee.Kind == ExpressionErrorKind.ExpectingLeftParen) { triggerLength = fn.Name.Length; return(TriggerState.ItemFunctionName); } break; case ExpressionPropertyFunctionInvocation pfi: if (iee.Kind == ExpressionErrorKind.ExpectingMethodName) { return(TriggerState.PropertyFunctionName); } if (iee.Kind == ExpressionErrorKind.ExpectingClassName) { return(TriggerState.PropertyFunctionClassName); } break; case ExpressionClassReference cr: if (iee.Kind == ExpressionErrorKind.ExpectingBracketColonColon) { triggerLength = cr.Name.Length; return(TriggerState.PropertyFunctionClassName); } break; case ExpressionMetadata m: if (iee.Kind == ExpressionErrorKind.ExpectingMetadataName) { return(TriggerState.Metadata); } if (iee.Kind == ExpressionErrorKind.ExpectingRightParenOrPeriod) { triggerLength = m.ItemName.Length; return(TriggerState.MetadataOrItem); } if (iee.Kind == ExpressionErrorKind.ExpectingRightParen) { triggerLength = m.MetadataName.Length; return(TriggerState.Metadata); } break; } return(TriggerState.None); } if (error != null) { switch (error.Kind) { case ExpressionErrorKind.ExpectingPropertyName: return(TriggerState.Property); case ExpressionErrorKind.ExpectingItemName: return(TriggerState.Item); case ExpressionErrorKind.ExpectingMetadataOrItemName: return(TriggerState.MetadataOrItem); } return(TriggerState.None); } return(TriggerState.None); char LastChar() => expression[expression.Length - 1]; char PenultimateChar() => expression[expression.Length - 2]; bool IsPossiblePathSegment(char c) => c == '_' || char.IsLetterOrDigit(c) || c == '.'; }
static TriggerState GetTriggerState(string expression, out int triggerLength, out ExpressionNode triggerExpression) { triggerLength = 0; if (expression.Length == 0) { triggerExpression = new ExpressionText(0, "", true); return(TriggerState.Value); } if (expression.Length == 1) { triggerExpression = new ExpressionText(0, expression, true); triggerLength = 1; return(TriggerState.Value); } const ExpressionOptions options = ExpressionOptions.ItemsMetadataAndLists | ExpressionOptions.CommaLists; triggerExpression = ExpressionParser.Parse(expression, options); if (triggerExpression is ExpressionList el) { //the last list entry is the thing that triggered it triggerExpression = el.Nodes.Last(); if (triggerExpression is ExpressionError e && e.Kind == ExpressionErrorKind.EmptyListEntry) { return(LastChar() == ',' ? TriggerState.CommaValue : TriggerState.SemicolonValue); } if (triggerExpression is ExpressionText l) { if (l.Length == 1) { triggerLength = 1; return(PenultimateChar() == ',' ? TriggerState.CommaValue : TriggerState.SemicolonValue); } } } var lastNode = triggerExpression; if (lastNode is Expression expr) { lastNode = expr.Nodes.Last(); } if (lastNode is ExpressionText lit) { if (LastChar() == '\\') { return(TriggerState.DirectorySeparator); } if (lit.Value.Length >= 2 && PenultimateChar() == '\\' && IsPossiblePathSegment(LastChar())) { triggerLength = 1; return(TriggerState.DirectorySeparator); } } if (lastNode is IncompleteExpressionError iee && iee.WasEOF) { switch (iee.IncompleteNode) { case ExpressionItem i: if (iee.Kind == ExpressionErrorKind.ExpectingRightParenOrDash && i.Name.Length == 1) { triggerLength = 1; return(TriggerState.Item); } break; case ExpressionProperty p: if (iee.Kind == ExpressionErrorKind.ExpectingRightParenOrPeriod && p.Name.Length == 1) { triggerLength = 1; return(TriggerState.Property); } if (iee.Kind == ExpressionErrorKind.ExpectingMethodName) { return(TriggerState.MethodName); } if (iee.Kind == ExpressionErrorKind.ExpectingLeftParen) { var inv = p.Find(iee.Offset - 1) as ExpressionPropertyFunctionInvocation; if (inv != null && inv.MethodName != null && inv.MethodName.Length == 1) { triggerLength = 1; return(TriggerState.MethodName); } } break; case ExpressionMetadata m: if (iee.Kind == ExpressionErrorKind.ExpectingMetadataName) { return(TriggerState.Metadata); } if (iee.Kind == ExpressionErrorKind.ExpectingRightParenOrPeriod && m.ItemName.Length == 1) { triggerLength = 1; return(TriggerState.MetadataOrItem); } if (iee.Kind == ExpressionErrorKind.ExpectingRightParen && m.MetadataName.Length == 1) { triggerLength = 1; return(TriggerState.Metadata); } break; } return(TriggerState.None); } if (lastNode is ExpressionError err) { switch (err.Kind) { case ExpressionErrorKind.ExpectingPropertyName: return(TriggerState.Property); case ExpressionErrorKind.ExpectingItemName: return(TriggerState.Item); case ExpressionErrorKind.ExpectingMetadataOrItemName: return(TriggerState.MetadataOrItem); } return(TriggerState.None); } return(TriggerState.None); char LastChar() => expression[expression.Length - 1]; char PenultimateChar() => expression[expression.Length - 2]; bool IsPossiblePathSegment(char c) => c == '_' || char.IsLetterOrDigit(c) || c == '.'; }
void ExtractReferences(MSBuildValueKind kind, string value, int startOffset) { try { var expression = ExpressionParser.Parse(value, ExpressionOptions.ItemsMetadataAndLists, startOffset); foreach (var node in expression.WithAllDescendants()) { switch (node) { case ExpressionProperty prop: if (prop.IsSimpleProperty) { CollectProperty(prop.Name); } break; case ExpressionItem item: CollectItem(item.Name); break; case ExpressionMetadata meta: var itemName = meta.GetItemName(); if (itemName != null) { CollectMetadata(itemName, meta.MetadataName); } break; case ExpressionText literal: if (literal.IsPure) { value = literal.GetUnescapedValue().Trim(); switch (kind.GetScalarType()) { case MSBuildValueKind.ItemName: CollectItem(value); break; case MSBuildValueKind.TargetName: CollectTarget(value); break; case MSBuildValueKind.PropertyName: CollectProperty(value); break; case MSBuildValueKind.Configuration: Document.Configurations.Add(value); break; case MSBuildValueKind.Platform: Document.Platforms.Add(value); break; } } break; } } } catch (Exception ex) { LoggingService.LogError($"Error parsing MSBuild expression '{value}' in file {Filename} at {startOffset}", ex); } }