static int FindNextChild(TrackingSpanNode <T> root, SnapshotPoint point, int currentChildIndex) { // If we're already at the end, there's no need to continue searching if (currentChildIndex == root.Children.Count - 1) { return(currentChildIndex + 1); } return(FindChild(point, root.Children, left: true, lo: currentChildIndex + 1).Index); }
/// <summary> /// Create a tracking span tree for the given buffer. /// </summary> /// <param name="buffer">The buffer that all the spans in this tree are in.</param> /// <param name="keepTrackingCurrent">The tree should not allow tracking spans to point /// to old versions, at the expense of walking the tree on every text change.</param> public TrackingSpanTree(ITextBuffer buffer, bool keepTrackingCurrent) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } Buffer = buffer; Count = 0; // Leave the tracking span parameter as null, since it is // never used (assumed to always be the entire buffer) Root = new TrackingSpanNode <T>(default(T), null); if (keepTrackingCurrent) { buffer.Changed += OnBufferChanged; } }
static bool RemoveItemFromRoot(T item, SnapshotSpan span, TrackingSpanNode <T> root) { if (root.Children.Count == 0) { return(false); } var result = FindChild(span.Start, root.Children, left: true); if (result.Index < 0 || result.Index >= root.Children.Count) { return(false); } // Search from this index onward (there may be empty regions in the way) for (int i = result.Index; i < root.Children.Count; i++) { var child = root.Children[i]; SnapshotSpan childSpan = child.TrackingSpan.GetSpan(span.Snapshot); // Check to see if we've walked past it if (childSpan.Start > span.End) { return(false); } else if (childSpan == span && object.Equals(child.Item, item)) { root.Children.RemoveAt(i); root.Children.InsertRange(i, child.Children); return(true); } else if (childSpan.Contains(span)) { if (RemoveItemFromRoot(item, span, child)) { return(true); } } } return(false); }
/// <summary> /// Try to add an item to the tree with the given tracking span. /// </summary> /// <param name="item">The item to add to the tree.</param> /// <param name="trackingSpan">The tracking span it is associated with.</param> /// <returns>The newly added node, if the item was successfully added; <c>null</c> if adding the item to the tree would /// violate the well-formedness of the tree.</returns> /// <exception cref="ArgumentNullException">If <paramref name="trackingSpan"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If the tracking mode of <paramref name="trackingSpan"/> is not <see cref="SpanTrackingMode.EdgeExclusive"/>.</exception> public TrackingSpanNode <T> TryAddItem(T item, ITrackingSpan trackingSpan) { if (trackingSpan == null) { throw new ArgumentNullException(nameof(trackingSpan)); } if (trackingSpan.TrackingMode != SpanTrackingMode.EdgeExclusive) { throw new ArgumentException("The tracking mode of the given tracking span must be SpanTrackingMode.EdgeExclusive", nameof(trackingSpan)); } SnapshotSpan spanToAdd = trackingSpan.GetSpan(Buffer.CurrentSnapshot); TrackingSpanNode <T> node = new TrackingSpanNode <T>(item, trackingSpan); var newNode = TryAddNodeToRoot(node, spanToAdd, Root); if (newNode != null) { Count++; } return(newNode); }
static TrackingSpanNode <T> TryAddNodeToRoot(TrackingSpanNode <T> newNode, SnapshotSpan span, TrackingSpanNode <T> root) { var children = root.Children; if (children.Count == 0) { children.Add(newNode); return(newNode); } FindResult leftResult = FindIndexForAdd(span.Start, children, left: true); FindResult rightResult = FindIndexForAdd(span.End, children, left: false); // See if we can add the node anywhere // The indices cross if the searches fail, and the node is inside a gap. if (leftResult.Index > rightResult.Index) { // Case #1: If the new node should go in a gap between two nodes, just insert it in the correct location Debug.Assert(leftResult.Type == FindResultType.Outer || rightResult.Type == FindResultType.Outer); children.Insert(leftResult.Index, newNode); return(newNode); } else { if (leftResult.Type == FindResultType.Inner || rightResult.Type == FindResultType.Inner) { // Case #2: The new node is contained entirely in a single child node, so add it to that child // Check the nodes at either end of the resulting indices (they may not be the same index due to // 0-length nodes that abut the correct child). if (children[leftResult.Index].TrackingSpan.GetSpan(span.Snapshot).Contains(span)) { return(TryAddNodeToRoot(newNode, span, children[leftResult.Index])); } if (leftResult.Index != rightResult.Index && children[rightResult.Index].TrackingSpan.GetSpan(span.Snapshot).Contains(span)) { return(TryAddNodeToRoot(newNode, span, children[rightResult.Index])); } // This fails if the node isn't fully contained in a single child } else { // Case #3: The new node contains any number of children, so we: int start = leftResult.Index; int count = rightResult.Index - leftResult.Index + 1; // a) Add all the children this should contain to the new node, newNode.Children.AddRange(children.Skip(start).Take(count)); // b) Remove them from the existing root node, and children.RemoveRange(start, count); // c) Add the new node in their place children.Insert(start, newNode); return(newNode); } } // We couldn't find a place to add this node, so return failure return(null); }
static IEnumerable <TrackingSpanNode <T> > FindNodes(NormalizedSnapshotSpanCollection spans, TrackingSpanNode <T> root, bool recurse = true, bool contained = false) { if (spans == null || spans.Count == 0 || root.Children.Count == 0) { yield break; } int requestIndex = 0; SnapshotSpan currentRequest = spans[requestIndex]; // Find the first child FindResult findResult = FindChild(currentRequest.Start, root.Children, left: true); int childIndex = findResult.Index; if (childIndex >= root.Children.Count) { yield break; } ITextSnapshot snapshot = currentRequest.Snapshot; SnapshotSpan currentChild = root.Children[childIndex].TrackingSpan.GetSpan(snapshot); while (requestIndex < spans.Count && childIndex < root.Children.Count) { if (currentRequest.Start > currentChild.End) { // Find the next child childIndex = FindNextChild(root, currentRequest.Start, childIndex); if (childIndex < root.Children.Count) { currentChild = root.Children[childIndex].TrackingSpan.GetSpan(snapshot); } } else if (currentChild.Start > currentRequest.End) { // Skip to the next request if (++requestIndex < spans.Count) { currentRequest = spans[requestIndex]; } } else { // Yield the region then move to the next if (!contained || currentRequest.Contains(currentChild)) { yield return(root.Children[childIndex]); } if (recurse) { foreach (var result in FindNodes(spans, root.Children[childIndex], recurse, contained)) { yield return(result); } } // Find the next child childIndex = FindNextChild(root, currentRequest.Start, childIndex); if (childIndex < root.Children.Count) { currentChild = root.Children[childIndex].TrackingSpan.GetSpan(snapshot); } } } }
/// <summary> /// Check if a given node is a toplevel node (has no parents). /// </summary> /// <param name="node">The node to check.</param> /// <returns><c>true</c> if the node has no parent node.</returns> public bool IsNodeTopLevel(TrackingSpanNode <T> node) { return(Root.Children.Contains(node)); }