private void UpdateCachedTagsForBuffer(ITextSnapshot snapshot, TagSpanIntervalTree <TTag> newTagsForBuffer) { var oldCachedTags = _cachedTags; lock (_cachedTagsGate) { _cachedTags = _cachedTags.SetItem(snapshot.TextBuffer, newTagsForBuffer); } // Grab our old tags. We might not have any, so in this case we'll just pretend it's // empty TagSpanIntervalTree <TTag> oldCachedTagsForBuffer = null; if (!oldCachedTags.TryGetValue(snapshot.TextBuffer, out oldCachedTagsForBuffer)) { oldCachedTagsForBuffer = new TagSpanIntervalTree <TTag>(snapshot.TextBuffer, SpanTrackingMode.EdgeExclusive); } var difference = ComputeDifference(snapshot, oldCachedTagsForBuffer, newTagsForBuffer); if (difference.Count > 0) { RaiseTagsChanged(snapshot.TextBuffer, difference); } }
private NormalizedSnapshotSpanCollection ComputeDifference( ITextSnapshot snapshot, TagSpanIntervalTree <TTag> latestSpans, TagSpanIntervalTree <TTag> previousSpans) { return(new NormalizedSnapshotSpanCollection( Difference(latestSpans.GetSpans(snapshot), previousSpans.GetSpans(snapshot), new DiffSpanComparer(_tagProducer.TagComparer)))); }
private bool TryStealTagsFromRelatedTagSource(TextContentChangedEventArgs e) { // see bug 778731 #if INTERACTIVE // If we don't have a way to find the related buffer, we're done immediately if (bufferToRelatedTagSource == null) { return(false); } // We can only steal tags if we know where the edit came from, so do we? var editTag = e.EditTag as RestoreHistoryEditTag; if (editTag == null) { return(false); } var originalSpan = editTag.OriginalSpan; var relatedTagSource = bufferToRelatedTagSource(originalSpan.Snapshot.TextBuffer); if (relatedTagSource == null) { return(false); } // Reading the other tag source's cached tags is safe, since this field is allowed to be // accessed from multiple threads and is immutable. We still need to have a local copy // though to play it safe and be a good citizen (well, as good as a citizen that's about // to steal something can be...) var relatedCachedTags = relatedTagSource.cachedTags; TagSpanIntervalTree <TTag> relatedIntervalTree; if (!relatedCachedTags.TryGetValue(originalSpan.Snapshot.TextBuffer, out relatedIntervalTree)) { return(false); } // Excellent! Let's build a new interval tree with these tags mapped to our buffer // instead var tagsForThisBuffer = from tagSpan in relatedIntervalTree.GetSpans(originalSpan.Snapshot) where tagSpan.Span.IntersectsWith(originalSpan) let snapshotSpan = new SnapshotSpan(e.After, tagSpan.SpanStart - originalSpan.Start, tagSpan.Span.Length) select new TagSpan <TTag>(snapshotSpan, tagSpan.Tag); var intervalTreeForThisBuffer = new TagSpanIntervalTree <TTag>(e.After.TextBuffer, relatedIntervalTree.SpanTrackingMode, tagsForThisBuffer); // Update our cached tags UpdateCachedTagsForBuffer(e.After, intervalTreeForThisBuffer); return(true); #else return(false); #endif }
private void UpdateTagsForTextChange(TextContentChangedEventArgs e) { if (!e.Changes.Any()) { return; } // We might be able to steal the cached tags from another tag source if (TryStealTagsFromRelatedTagSource(e)) { return; } var buffer = e.After.TextBuffer; TagSpanIntervalTree <TTag> treeForBuffer; if (!_cachedTags.TryGetValue(buffer, out treeForBuffer)) { return; } if (!_removeTagsThatIntersectEdits) { return; } var tagsToRemove = e.Changes.SelectMany(c => treeForBuffer.GetIntersectingSpans(new SnapshotSpan(e.After, c.NewSpan))); if (!tagsToRemove.Any()) { return; } var allTags = treeForBuffer.GetSpans(e.After).ToList(); var newTreeForBuffer = new TagSpanIntervalTree <TTag>( buffer, treeForBuffer.SpanTrackingMode, allTags.Except(tagsToRemove, this.TagSpanComparer)); UpdateCachedTagsForBuffer(e.After, newTreeForBuffer); }