internal ITextSnapshot AdvanceToCurrentSnapshot() { //We don't need to take a snapshot of the results because the results are only modified on this thread. ITextSnapshot oldSnapshot = _results.Snapshot; ITextSnapshot newSnapshot = _buffer.CurrentSnapshot; if (oldSnapshot != newSnapshot) { //The results are all on an old snapshot. We need to project them forward (even though that might cause some stale and incorrect //results). NormalizedSpanCollection newMatches = TextSearchNavigator.TranslateTo(oldSnapshot, _results.Matches, newSnapshot); NormalizedSpanCollection newSearchedSpans = NormalizedSpanCollection.Empty; if ((_results.SearchedSpans.Count != 0) && !_options.HasFlag(FindOptions.Multiline)) { //Advance our record of the spans that have already been searched to the new snapshot as well. newSearchedSpans = BackgroundSearch <T> .TranslateToAndExtend(oldSnapshot, _results.SearchedSpans, newSnapshot); //But remove anything on a TextSnapshotLine that was modified by the change. List <Span> changedSpansOnNewSnapshot = new List <Span>(); ITextVersion version = oldSnapshot.Version; while (version != newSnapshot.Version) { foreach (var change in version.Changes) { changedSpansOnNewSnapshot.Add(BackgroundSearch <T> .Extend(newSnapshot, Tracking.TrackSpanForwardInTime(SpanTrackingMode.EdgeInclusive, change.NewSpan, version.Next, newSnapshot.Version))); } version = version.Next; } if (changedSpansOnNewSnapshot.Count > 0) { NormalizedSpanCollection changes = new NormalizedSpanCollection(changedSpansOnNewSnapshot); //Remove the spans touched by changes from the spans we've searched newSearchedSpans = NormalizedSpanCollection.Difference(newSearchedSpans, changes); } } _results = new SearchResults(newSnapshot, newMatches, newSearchedSpans); } return(newSnapshot); }
public static NormalizedSpanCollection TranslateToAndExtend(ITextSnapshot currentSnapshot, NormalizedSpanCollection currentSpans, ITextSnapshot targetSnapshot) { if (currentSpans.Count == 0) { return(currentSpans); } List <Span> spans = new List <Span>(currentSpans.Count); foreach (var s in currentSpans) { spans.Add(BackgroundSearch <T> .Extend(targetSnapshot, Tracking.TrackSpanForwardInTime(SpanTrackingMode.EdgeNegative, s, currentSnapshot.Version, targetSnapshot.Version))); } return(new NormalizedSpanCollection(spans)); }
internal void ProcessQueue() { // Ensure the thread that is doing the work is both low priority and also background try { Thread.CurrentThread.Priority = ThreadPriority.Lowest; //Only one instance of this thread is running at a time, so we don't need to put locks around the bits that update //our state (only the bits that play with the results queue). while (true) { if (_isDisposed) { return; } NormalizedSnapshotSpanCollection request; lock (_requestQueue) { //Do not dequeue the result here ... if a new request comes in while we are processing this request, //we do not want to start a new thread. request = _requestQueue.Peek(); } //Always do searches on the current snapshot of the buffer, migrating results to that snapshot //if needed. ITextSnapshot snapshot = this.AdvanceToCurrentSnapshot(); NormalizedSpanCollection requestedSpans; if (_options.HasFlag(FindOptions.Multiline)) { //Multi-line searches are all or nothing. if (_results.SearchedSpans.Count == 0) { requestedSpans = new NormalizedSpanCollection(new Span(0, snapshot.Length)); } else { Debug.Assert((_results.SearchedSpans.Count == 1) && (_results.SearchedSpans[0].Start == 0) && (_results.SearchedSpans[0].End == snapshot.Length)); requestedSpans = NormalizedSpanCollection.Empty; } } else { requestedSpans = BackgroundSearch <T> .TranslateToAndExtend(request[0].Snapshot, request, snapshot); if (_results.SearchedSpans.Count > 0) { if ((_results.SearchedSpans.Count == 1) && (_results.SearchedSpans[0].Start == 0) && (_results.SearchedSpans[0].End == snapshot.Length)) { //We've already got results for the entire buffer. requestedSpans = NormalizedSpanCollection.Empty; } else { requestedSpans = NormalizedSpanCollection.Difference(requestedSpans, _results.SearchedSpans); } } } bool dequeueRequest = true; if (requestedSpans.Count > 0) { IList <Span> newMatches = this.FindAll(snapshot, requestedSpans); if (_isDisposed) { return; } if (snapshot == _buffer.CurrentSnapshot) { //The search completed without the buffer changing out from under us, add in the new results. //Remove any stale results in the places we searched (since we do not remove potentially stale results //on a text change, we have to remove them here) and then add in the results we found. if (_options.HasFlag(FindOptions.Multiline)) { //Multiline searches are always whole buffer searches, so we can skip the set operations. Debug.Assert(requestedSpans.Count == 1); Debug.Assert(requestedSpans[0].Start == 0); Debug.Assert(requestedSpans[0].Length == snapshot.Length); _results = new SearchResults(snapshot, new NormalizedSpanCollection(newMatches), new NormalizedSpanCollection(new Span(0, snapshot.Length))); } else { //Remove the stale results. NormalizedSpanCollection m = NormalizedSpanCollection.Difference(_results.Matches, requestedSpans); //Add in the new results. if (newMatches.Count > 0) { m = NormalizedSpanCollection.Union(m, new NormalizedSpanCollection(newMatches)); } //Save the results _results = new SearchResults(snapshot, m, NormalizedSpanCollection.Union(_results.SearchedSpans, requestedSpans)); } //We completed the search & updated the results ... have the tagger to raise the appropriate changed event //on the span we just searched. // //We can't raise the tags changed on just the results since we also need to signal that stale results have //been removed. _callback(snapshot, requestedSpans); } else { //The buffer changed so we can't trust the results we just got (the search may not have completed). //Don't dequeue the request and we'll repeat the process (but on the correct snapshot). dequeueRequest = false; } } if (dequeueRequest) { lock (_requestQueue) { //Nothing should have moved the request out of the queue. Debug.Assert(object.ReferenceEquals(request, _requestQueue.Peek())); _requestQueue.Dequeue(); if (_requestQueue.Count == 0) { //No more requests are pending, release the worker thread. return; } } } } } finally { Thread.CurrentThread.Priority = ThreadPriority.Normal; } }