/// <summary> /// Gets the common ancestor of this item and one other. /// </summary> /// <param name="otherNode"> /// The other node. /// </param> /// <returns> /// The common ancestor. /// </returns> public XamlNode GetCommonAncestor(XamlNode otherNode) { logger.Trace("Entered GetCommonAncestor()"); return((from ancestor in this.Ancestors from othersAncestor in otherNode.Ancestors where ancestor == othersAncestor select ancestor).FirstOrDefault()); }
/// <summary> /// Gets the common ancestor of this item and one other. /// </summary> /// <param name="otherNode"> /// The other node. /// </param> /// <returns> /// The common ancestor. /// </returns> public XamlNode GetCommonAncestor(XamlNode otherNode) { foreach (var ancestor in this.Ancestors) { foreach (var othersAncestor in otherNode.Ancestors) { if (ancestor == othersAncestor) { return(ancestor); } } } return(null); }
/// <summary> /// A TextSelection extension method that select parent. /// </summary> /// <param name="selection"> /// The target to act on. /// </param> /// <returns> /// true if it succeeds, otherwise false. /// </returns> public static bool SelectParent(this TextSelection selection) { EditorPoints ep = selection.GetEditorPoints(); XamlNode[] nodes = _rootNode.GetSelectedNodes(ep); if (nodes == null || nodes == null) { return(false); } XamlNode parent = nodes[0].Parent; selection.SetSelection(parent.BottomPoint + 1, parent.TopPoint + 1); return(true); }
/// <summary> /// Access set to private to prevent usage from outside. Called only by /// XamlNode constructors. /// </summary> /// <exception cref="ArgumentException"> /// Thrown when one or more arguments have unsupported or illegal values. /// </exception> /// <exception cref="ApplicationException"> /// Thrown when an Application error condition occurs. /// </exception> /// <param name="newStartTag"> /// The new start tag. /// </param> /// <param name="parent"> /// The parent. /// </param> private XamlNode(XamlTag newStartTag, XamlNode parent) { logger.Trace("Entered XamlNode()"); if (newStartTag.TagType == XamlTagType.Unknown) { throw new ArgumentException("The startingTag given is invalid.", "newStartTag"); } if (newStartTag.TagType == XamlTagType.Ending) { throw new ArgumentException("And ending tag was given instead of a starting tag.", "newStartTag"); } this.startTag = newStartTag; this.parent = parent; if (newStartTag.TagType == XamlTagType.SelfClosing) { // Self closing tags have no children or separate closing tags. this.endTag = newStartTag; return; } while (tagMatchesEnumerator.MoveNext()) { var childTag = new XamlTag(tagMatchesEnumerator.Current); if (childTag.TagType == XamlTagType.Ending) { if (childTag.Name != this.Name) { throw new ArgumentException( string.Format("Expected \"{0}\" for end tag, but encountered \"{1}.\"", this.Name, childTag.Name)); } this.endTag = childTag; return; } var childNode = new XamlNode(childTag, this) { DocumentText = this.documentText }; this.ChildrenContainer.AddLast(childNode); } // the end of tags represent the end of xaml tags. Since this builds a // child, it should never reach this point. throw new ApplicationException("Premature end of tags encountered."); }
/// <summary> /// This method should return a tupple with two nodes without fail (if /// it is called through the root node). The /// first node should be the node in which the topPoint falls, if it /// falls within one of it's tags, after the last tag of its last child, /// if it has no children. The second node should be the node in /// which the bottom point falls, if it falls within one of its tags, /// before the first tag of its first child, or if it has no children. /// </summary> /// <param name="topPoint"> /// The top point. /// </param> /// <param name="bottomPoint"> /// The bottom point. /// </param> /// <returns> /// The first last nodes between points. /// </returns> public Tuple <XamlNode, XamlNode> GetFirstLastNodesBetweenPoints(int topPoint, int bottomPoint) { if (topPoint == bottomPoint) { XamlNode node = this.GetNodeWithOffset(topPoint); return(new Tuple <XamlNode, XamlNode>(node, node)); } PositionAndNode pan1 = this.GetTopPointPositionAndNode(topPoint); PositionAndNode pan2 = this.GetBottomPointPositionAndNode(bottomPoint); if (pan1.Node.Parent != pan2.Node.Parent) { XamlNode parent = pan1.Node.GetCommonAncestor(pan2.Node); XamlNode node1 = parent.GetChildNodeWithOffset(topPoint); XamlNode node2 = parent.GetChildNodeWithOffset(bottomPoint); return(new Tuple <XamlNode, XamlNode>(node1, node2)); } return(new Tuple <XamlNode, XamlNode>(pan1.Node, pan2.Node)); }
private static EditorPoints GetEditorPoints(TextSelection sel, bool handleExceptions = true) { EditorPoints ep = sel.GetEditorPoints(); try { if (_documentText == ep.DocumentText) { return(ep); } _documentText = ep.DocumentText; _rootNode = new XamlNode(_documentText); return(ep); } catch { ep.RestoreSelectedText(sel); return(EditorPoints.GetInvalidEditorPoints()); } }
/// <summary> /// A TextSelection extension method that selects a single node. /// </summary> /// <param name="selection"> /// The target to act on. /// </param> /// <returns> /// true if it succeeds, otherwise false. /// </returns> public static bool SelectNode(this TextSelection selection) { // Works only when the selected text is empty. if (!selection.IsEmpty) { return(false); } // This has to be done here primarily to prime the data // needed by called methods. Othwerwise we could just // reference the offsets directly. EditorPoints ep = GetEditorPoints(selection); XamlNode node = _rootNode.GetNodeWithOffset(ep.ActivePoint - 1); if (node == null) { return(false); } selection.SetSelection(node.BottomPoint + 1, node.TopPoint + 1); return(true); }
/// <summary> /// Gets node with offset. /// </summary> /// <param name="offset"> /// The document point. /// </param> /// <returns> /// The node with offset. /// </returns> public XamlNode GetNodeWithOffset(int offset) { if (offset <= this.TopPoint || offset >= this.BottomPoint) { // documentPoint is not in this node or its children. return(null); } foreach (var child in this.ChildrenContainer) { XamlNode descendant = child.GetNodeWithOffset(offset); if (descendant == null) { continue; } // documentPoint is within this child. return(descendant); } // documentPoint is within this node, but not in any of its // children. return(this); }
/// <summary> /// Initializes a new instance of the PositionAndNode class. /// </summary> /// <param name="node"> /// The node. /// </param> /// <param name="positionIndex"> /// Zero-based index of the position. /// </param> public PositionAndNode(XamlNode node, int positionIndex) { logger.Trace("Entered PositionAndNode()"); this.Node = node; this.PositionIndex = positionIndex; }
/// <summary> /// Initializes a new instance of the XamlNode class. used to create a /// root node. /// </summary> /// <exception cref="ApplicationException"> /// Thrown when an Application error condition occurs. /// </exception> /// <param name="documentText"> /// The document text. /// </param> /// <remarks> /// Create the root node of a xaml file with this constructor. All other /// child nodes will be created as a result. /// </remarks> public XamlNode(string documentText) { // The document to be parsed for building a XamlNode tree. this._documentText = documentText; this._isRoot = true; // This object holds a sorted list of Match objects that correspond // to each tag in the document. _sortedTagMatches = new SortedSet <Match>(new XamlTagMatchComparer()); MatchCollection matches = Regex.Matches(documentText, XmlHelpers.STARTTAG_PATTERN, RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.IgnoreCase); foreach (Match item in matches) { _sortedTagMatches.Add(item); } matches = Regex.Matches(documentText, XmlHelpers.ENDTAG_PATTERN, RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.IgnoreCase); foreach (Match item in matches) { _sortedTagMatches.Add(item); } // the _sortedTagMatches collection is complete. That is now used // to build out the tree starting with this node, the root node. _tagMatchesEnumerator = _sortedTagMatches.GetEnumerator(); if (_tagMatchesEnumerator.MoveNext()) { this._startTag = new XamlTag(_tagMatchesEnumerator.Current); if (this._startTag.TagType != XamlTagType.Starting) { throw new ApplicationException("Starting tag expected."); } } else { throw new ApplicationException("Unable to enumerate Xaml tags."); } // Except for this node's end tag, the only thing it will see in // this loop are its children node. The child nodes work similarly, // so by the time they return, the nodes that belong to the children // will be consumed. while (_tagMatchesEnumerator.MoveNext()) { var childTag = new XamlTag(_tagMatchesEnumerator.Current); if (childTag.TagType == XamlTagType.Unknown) { throw new ApplicationException("Unknown tag type encountered."); } if (childTag.TagType == XamlTagType.Ending) { this._endTag = childTag; // This is the end of the root node, so this should be the end // of the xaml tags. if (_tagMatchesEnumerator.MoveNext()) { throw new ApplicationException("Closing tag encountered before last tag."); } break; } var childNode = new XamlNode(childTag, this) { DocumentText = this._documentText }; this.ChildrenContainer.AddLast(childNode); } _tagMatchesEnumerator.Dispose(); _sortedTagMatches = null; }