/// <summary> /// Adds suggestions for a given node. /// </summary> /// <param name="node">Node for which suggestions are needed.</param> /// <param name="hasSpecificSuggestions">Flag to indicate if inner most function has any specific suggestions.</param> /// <param name="currentNode">Current node in the traversal.</param> public static bool AddTopLevelSuggestionsForGivenNode(IntellisenseData.IntellisenseData intellisenseData, TexlNode node, TexlNode currentNode) { Contracts.AssertValue(intellisenseData); Contracts.AssertValue(node); Contracts.AssertValue(currentNode); CallNode callNode = GetNearestCallNode(currentNode); if (callNode == null) { return(false); } if (callNode.Args.Count == 0) { AddTopLevelSuggestionsForCursorType(intellisenseData, callNode, 0); return(TryAddSpecificSuggestionsForGivenArgPosition(intellisenseData, callNode, 0)); } for (int i = 0; i < callNode.Args.Count; i++) { if (node.InTree(callNode.Args.Children[i])) { AddTopLevelSuggestionsForCursorType(intellisenseData, callNode, i); if (Object.ReferenceEquals(node, currentNode) && TryAddSpecificSuggestionsForGivenArgPosition(intellisenseData, callNode, i)) { return(true); } } } if (callNode.Parent != null) { return(AddTopLevelSuggestionsForGivenNode(intellisenseData, node, callNode.Parent)); } return(false); }
private bool IsArgTypeInconsequential(TexlNode arg) { Contracts.AssertValue(arg); Contracts.Assert(arg.Parent is ListNode); Contracts.Assert(arg.Parent.Parent is CallNode); Contracts.Assert(arg.Parent.Parent.AsCall().Head.Name == Name); CallNode call = arg.Parent.Parent.AsCall().VerifyValue(); // Pattern: OnSelect = If(cond, argT, argF) // Pattern: OnSelect = If(cond, arg1, cond, arg2, ..., argK, argF) // Pattern: OnSelect = If(cond, arg1, If(cond, argT, argF)) // Pattern: OnSelect = If(cond, arg1, If(cond, arg2, cond, arg3, ...)) // Pattern: OnSelect = If(cond, arg1, cond, If(cond, arg2, cond, arg3, ...), ...) // ...etc. CallNode ancestor = call; while (ancestor.Head.Name == Name) { if (ancestor.Parent == null && ancestor.Args.Children.Length > 0) { for (int i = 0; i < ancestor.Args.Children.Length; i += 2) { // If the given node is part of a condition arg of an outer If invocation, // then it's NOT inconsequential. Note that the very last arg to an If // is not a condition -- it's the "else" branch, hence the test below. if (i != ancestor.Args.Children.Length - 1 && arg.InTree(ancestor.Args.Children[i])) { return(false); } } return(true); } // Deal with the possibility that the ancestor may be contributing to a chain. // This also lets us cover the following patterns: // Pattern: OnSelect = X; If(cond, arg1, arg2); Y; Z // Pattern: OnSelect = X; If(cond, arg1;arg11;...;arg1k, arg2;arg21;...;arg2k); Y; Z // ...etc. VariadicOpNode chainNode; if ((chainNode = ancestor.Parent.AsVariadicOp()) != null && chainNode.Op == VariadicOp.Chain) { // Top-level chain in a behavior rule. if (chainNode.Parent == null) { return(true); } // A chain nested within a larger non-call structure. if (!(chainNode.Parent is ListNode) || !(chainNode.Parent.Parent is CallNode)) { return(false); } // Only the last chain segment is consequential. int numSegments = chainNode.Children.Length; if (numSegments > 0 && !arg.InTree(chainNode.Children[numSegments - 1])) { return(true); } // The node is in the last segment of a chain nested within a larger invocation. ancestor = chainNode.Parent.Parent.AsCall(); continue; } // Walk up the parent chain to the outer invocation. if (!(ancestor.Parent is ListNode) || !(ancestor.Parent.Parent is CallNode)) { return(false); } ancestor = ancestor.Parent.Parent.AsCall(); } // Exhausted all supported patterns. return(false); }
// In behavior properties, the arg type is irrelevant if nothing actually depends // on the output type of IfError (see If.cs, Switch.cs) private bool IsArgTypeInconsequential(TexlNode arg) { Contracts.AssertValue(arg); Contracts.Assert(arg.Parent is ListNode); Contracts.Assert(arg.Parent.Parent is CallNode); Contracts.Assert(arg.Parent.Parent.AsCall().Head.Name == Name); CallNode call = arg.Parent.Parent.AsCall().VerifyValue(); // Pattern: OnSelect = IfError(arg1, arg2, ... argK) // Pattern: OnSelect = IfError(arg1, IfError(arg1, arg2,...), ... argK) // ...etc. CallNode ancestor = call; while (ancestor.Head.Name == Name) { if (ancestor.Parent == null && ancestor.Args.Children.Length > 0) { return(true); } // Deal with the possibility that the ancestor may be contributing to a chain. // This also lets us cover the following patterns: // Pattern: OnSelect = X; IfError(arg1, arg2); Y; Z // Pattern: OnSelect = X; IfError(arg1;arg11;...;arg1k, arg2;arg21;...;arg2k); Y; Z // ...etc. VariadicOpNode chainNode; if ((chainNode = ancestor.Parent.AsVariadicOp()) != null && chainNode.Op == VariadicOp.Chain) { // Top-level chain in a behavior rule. if (chainNode.Parent == null) { return(true); } // A chain nested within a larger non-call structure. if (!(chainNode.Parent is ListNode) || !(chainNode.Parent.Parent is CallNode)) { return(false); } // Only the last chain segment is consequential. int numSegments = chainNode.Children.Length; if (numSegments > 0 && !arg.InTree(chainNode.Children[numSegments - 1])) { return(true); } // The node is in the last segment of a chain nested within a larger invocation. ancestor = chainNode.Parent.Parent.AsCall(); continue; } // Walk up the parent chain to the outer invocation. if (!(ancestor.Parent is ListNode) || !(ancestor.Parent.Parent is CallNode)) { return(false); } ancestor = ancestor.Parent.Parent.AsCall(); } // Exhausted all supported patterns. return(false); }