/// <summary> /// Given an R AST, and a range in that AST, determines the DataTip expression for that range. /// </summary> /// <returns> /// AST node corresponding to the DataTip expression, or <c>null</c> if there is no valid DataTip for the given range. /// </returns> /// <remarks> /// DataTip expression is defined as the outermost expression enclosing the range, such that the sequence /// of operators from that outermost expression to the innermost node still enclosing the range only consists /// of operators: <c>$ @ :: ::: [ [[</c>. Furthermore, in case of <c>[ [[</c>, only their left operands /// are considered. /// </remarks> public static IAstNode GetDataTipExpression(IAstNode ast, ITextRange range) { var node = ast.NodeFromRange(range, inclusiveEnd: true); if (node == null || !IsValidInitialNode(node)) { return(null); } // When the lookup starts at [ or [[, the immediate node is the token, but its parent is an Indexer node, // and the parent of the indexer is the Operator. The loop below assumes that Operator is the immediate // parent, so walk one level up before entering it in this case. var indexer = node.Parent as Indexer; if (indexer != null) { node = indexer; } // Walk the AST tree up to determine the expression that should be evaluated, according to the following rules: // - if current node is a child of $, @, :: or ::: (on either side of the operator), keep walking; // - if current node is a left child of [ or [[, keep walking; // - otherwise, stop. // Thus, for an expression like a::b$c@d, the entire expression will be considered when hovering over a, b, c or d. // But for a[b$c][[d]], hovering over a will consider a[b$c][d], but hovering over b or c will only consider b$c. while (true) { var parent = node.Parent as Operator; if (parent == null) { break; } var op = parent.OperatorType; if (op == OperatorType.Index) { // This is a[b] or a[[b]], and the current node is either a or b. If it is a, then we want to continue // walking up; but if it is b, we want to stop here, so that only b is shown. if (parent.Children.FirstOrDefault() != node) { break; } } else if (op != OperatorType.ListIndex && op != OperatorType.Namespace) { break; } node = parent; } return(node); }
/// <summary> /// Given an R AST, and a range in that AST, determines the DataTip expression for that range. /// </summary> /// <returns> /// AST node corresponding to the DataTip expression, or <c>null</c> if there is no valid DataTip for the given range. /// </returns> /// <remarks> /// DataTip expression is defined as the outermost expression enclosing the range, such that the sequence /// of operators from that outermost expression to the innermost node still enclosing the range only consists /// of operators: <c>$ @ :: ::: [ [[</c>. Furthermore, in case of <c>[ [[</c>, only their left operands /// are considered. /// </remarks> public static IAstNode GetDataTipExpression(IAstNode ast, ITextRange range) { var node = ast.NodeFromRange(range, inclusiveEnd: true); if (node == null || !IsValidInitialNode(node)) { return null; } // When the lookup starts at [ or [[, the immediate node is the token, but its parent is an Indexer node, // and the parent of the indexer is the Operator. The loop below assumes that Operator is the immediate // parent, so walk one level up before entering it in this case. var indexer = node.Parent as Indexer; if (indexer != null) { node = indexer; } // Walk the AST tree up to determine the expression that should be evaluated, according to the following rules: // - if current node is a child of $, @, :: or ::: (on either side of the operator), keep walking; // - if current node is a left child of [ or [[, keep walking; // - otherwise, stop. // Thus, for an expression like a::b$c@d, the entire expression will be considered when hovering over a, b, c or d. // But for a[b$c][[d]], hovering over a will consider a[b$c][d], but hovering over b or c will only consider b$c. while (true) { var parent = node.Parent as Operator; if (parent == null) { break; } var op = parent.OperatorType; if (op == OperatorType.Index) { // This is a[b] or a[[b]], and the current node is either a or b. If it is a, then we want to continue // walking up; but if it is b, we want to stop here, so that only b is shown. if (parent.Children.FirstOrDefault() != node) { break; } } else if (op != OperatorType.ListIndex && op != OperatorType.Namespace) { break; } node = parent; } return node; }