public void Clear()
 {
     var col = new NormalizedLineRangeCollection();
     col.Add(new LineRange(1, 2));
     col.Clear();
     Assert.Equal(0, col.Count);
 }
            public void Simple()
            {
                var visited = new NormalizedLineRangeCollection();

                visited.Add(LineRange.CreateFromBounds(0, 2));
                Assert.Equal(LineRange.CreateFromBounds(0, 2), visited.OverarchingLineRange.Value);
            }
Ejemplo n.º 3
0
 internal BackgroundCacheData(SnapshotLineRange lineRange, ReadOnlyCollection <ITagSpan <TTag> > tagList)
 {
     Snapshot          = lineRange.Snapshot;
     VisitedCollection = new NormalizedLineRangeCollection();
     VisitedCollection.Add(lineRange.LineRange);
     TagList = tagList;
 }
        public void Clear()
        {
            var col = new NormalizedLineRangeCollection();

            col.Add(new LineRange(1, 2));
            col.Clear();
            Assert.Equal(0, col.Count);
        }
 public void Simple()
 {
     var visited = new NormalizedLineRangeCollection();
     visited.Add(LineRange.CreateFromBounds(0, 2));
     Assert.Equal(LineRange.CreateFromBounds(0, 2), visited.OverarchingLineRange.Value);
 }
 internal NormalizedLineRangeCollection Create(params LineRange[] lineRanges)
 {
     return(NormalizedLineRangeCollection.Create(lineRanges));
 }
Ejemplo n.º 7
0
 internal BackgroundCacheData(ITextSnapshot snapshot, NormalizedLineRangeCollection visitedCollection, ReadOnlyCollection <ITagSpan <TTag> > tagList)
 {
     Snapshot          = snapshot;
     VisitedCollection = visitedCollection;
     TagList           = tagList;
 }
Ejemplo n.º 8
0
        private static void GetTagsInBackgroundCore(
            IAsyncTaggerSource <TData, TTag> asyncTaggerSource,
            TData data,
            int chunkCount,
            Channel channel,
            NormalizedLineRangeCollection visited,
            CancellationToken cancellationToken,
            Action <CompleteReason> onComplete,
            Action <SnapshotLineRange, ReadOnlyCollection <ITagSpan <TTag> > > onProgress)
        {
            CompleteReason completeReason;

            try
            {
                // Keep track of the LineRange values which we've already provided tags for.  Don't
                // duplicate the work
                var toProcess = new Queue <SnapshotLineRange>();

                // *** This value can be wrong ***
                // This is the version number we expect the Channel to have.  It's used
                // as a hueristic to determine if we should prioritize a value off of the stack or our
                // local stack.  If it's wrong it means we prioritize the wrong value.  Not a bug it
                // just changes the order in which values will appear
                var versionNumber = channel.CurrentVersion;

                // Take one value off of the threadedLineRangeStack value.  If the value is bigger than
                // our chunking increment then we will add the value in chunks to the toProcess queue
                Action popOne =
                    () =>
                {
                    var value = channel.Read();
                    if (!value.HasValue)
                    {
                        return;
                    }

                    var lineRange = value.Value;
                    if (lineRange.Count <= chunkCount)
                    {
                        toProcess.Enqueue(lineRange);
                        return;
                    }

                    var snapshot        = lineRange.Snapshot;
                    var startLineNumber = lineRange.StartLineNumber;
                    while (startLineNumber <= lineRange.LastLineNumber)
                    {
                        var startLine  = snapshot.GetLineFromLineNumber(startLineNumber);
                        var localRange = SnapshotLineRange.CreateForLineAndMaxCount(startLine, chunkCount);
                        toProcess.Enqueue(localRange);
                        startLineNumber += chunkCount;
                    }
                };

                // Get the tags for the specified SnapshotLineRange and return the results.  No chunking is done here,
                // the data is just directly processed
                Action <SnapshotLineRange> getTags =
                    tagLineRange =>
                {
                    var unvisited = visited.GetUnvisited(tagLineRange.LineRange);
                    if (unvisited.HasValue)
                    {
                        var tagList = EmptyTagList;
                        try
                        {
                            tagLineRange = SnapshotLineRange.CreateForLineNumberRange(tagLineRange.Snapshot, unvisited.Value.StartLineNumber, unvisited.Value.LastLineNumber).Value;
                            tagList      = asyncTaggerSource.GetTagsInBackground(data, tagLineRange.ExtentIncludingLineBreak, cancellationToken);
                        }
                        catch (Exception e)
                        {
                            // Ignore exceptions that are thrown by IAsyncTaggerSource.  If the tagger threw then we consider
                            // the tags to be nothing for this span.
                            //
                            // It's important that we register some value here.  If we register nothing then the foreground will
                            // never see this slot as fulfilled and later requests for this span will eventually queue up another
                            // background request
                            EditorUtilsTrace.TraceInfo("AsyncTagger source exception in background processing {0}", e);
                        }
                        visited.Add(tagLineRange.LineRange);
                        onProgress(tagLineRange, tagList);
                    }
                };

                do
                {
                    versionNumber = channel.CurrentVersion;
                    popOne();

                    // We've drained both of the sources of input hence we are done
                    if (0 == toProcess.Count)
                    {
                        break;
                    }

                    while (0 != toProcess.Count)
                    {
                        // If at any point the threadLineRangeStack value changes we consider the new values to have
                        // priority over the old ones
                        if (versionNumber != channel.CurrentVersion)
                        {
                            break;
                        }

                        cancellationToken.ThrowIfCancellationRequested();

                        var lineRange = toProcess.Dequeue();
                        getTags(lineRange);
                    }
                } while (!cancellationToken.IsCancellationRequested);

                completeReason = cancellationToken.IsCancellationRequested
                    ? CompleteReason.Cancelled
                    : CompleteReason.Finished;
            }
            catch (OperationCanceledException)
            {
                // Don't report cancellation exceptions.  These are thrown during cancellation for fast
                // break from the operation.  It's really a control flow mechanism
                completeReason = CompleteReason.Cancelled;
            }
            catch (Exception e)
            {
                // Handle cancellation exceptions and everything else.  Don't want an errant
                // exception thrown by the IAsyncTaggerSource to crash the process
                EditorUtilsTrace.TraceInfo("AsyncTagger Exception in background processing {0}", e);
                completeReason = CompleteReason.Error;
            }

            onComplete(completeReason);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Get the tags for the specified SnapshotSpan in a background task.  If there are outstanding
        /// requests for SnapshotSpan values then this one will take priority over those
        /// </summary>
        private void GetTagsInBackground(SnapshotSpan span)
        {
            var synchronizationContext = SynchronizationContext.Current;

            if (null == synchronizationContext)
            {
                return;
            }

            // The background processing should now be focussed on the specified ITextSnapshot
            var snapshot = span.Snapshot;

            // In the majority case GetTags(NormalizedSnapshotCollection) drives this function and
            // AdjustToSnapshot is already called.  There are other code paths though within AsyncTagger
            // which call this method.  We need to guard against them here.
            AdjustToSnapshot(snapshot);

            // Our caching and partitioning of data is all done on a line range
            // basis.  Just expand the requested SnapshotSpan to the encompassing
            // SnaphotlineRange
            var lineRange = SnapshotLineRange.CreateForSpan(span);

            span = lineRange.ExtentIncludingLineBreak;

            // If we already have a background task running for this ITextSnapshot then just enqueue this
            // request onto that existing one.  By this point if the request exists it must be tuned to
            // this ITextSnapshot
            if (_asyncBackgroundRequest.HasValue)
            {
                var asyncBackgroundRequest = _asyncBackgroundRequest.Value;
                if (asyncBackgroundRequest.Snapshot == snapshot)
                {
                    Contract.Requires(asyncBackgroundRequest.Snapshot == snapshot);
                    EditorUtilsTrace.TraceInfo("AsyncTagger Background Existing {0} - {1}", lineRange.StartLineNumber, lineRange.LastLineNumber);
                    asyncBackgroundRequest.Channel.WriteNormal(lineRange);
                    return;
                }

                CancelAsyncBackgroundRequest();
            }

            Contract.Assert(!_asyncBackgroundRequest.HasValue);
            EditorUtilsTrace.TraceInfo("AsyncTagger Background New {0} - {1}", lineRange.StartLineNumber, lineRange.LastLineNumber);

            // Create the data which is needed by the background request
            var data = _asyncTaggerSource.GetDataForSnapshot(snapshot);
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;
            var channel = new Channel();

            channel.WriteNormal(lineRange);

            // If there is an ITextView then make sure it is requested as well.  If the source provides an
            // ITextView then it is always prioritized on requests for a new snapshot
            if (_asyncTaggerSource.TextViewOptional != null)
            {
                var visibleLineRange = _asyncTaggerSource.TextViewOptional.GetVisibleSnapshotLineRange();
                if (visibleLineRange.HasValue)
                {
                    channel.WriteVisibleLines(visibleLineRange.Value);
                }
            }

            // The background thread needs to know the set of values we have already queried
            // for.  Send a copy since this data will be mutated from a background thread
            NormalizedLineRangeCollection localVisited;

            if (_tagCache.BackgroundCacheData.HasValue)
            {
                var backgroundCacheData = _tagCache.BackgroundCacheData.Value;
                Contract.Requires(backgroundCacheData.Snapshot == snapshot);
                localVisited = backgroundCacheData.VisitedCollection.Copy();
            }
            else
            {
                localVisited = new NormalizedLineRangeCollection();
            }

            // Function which finally gets the tags.  This is run on a background thread and can
            // throw as the implementor is encouraged to use CancellationToken::ThrowIfCancelled
            var    localAsyncTaggerSource = _asyncTaggerSource;
            var    localChunkCount        = _chunkCount;
            Action getTags = () => GetTagsInBackgroundCore(
                localAsyncTaggerSource,
                data,
                localChunkCount,
                channel,
                localVisited,
                cancellationToken,
                (completeReason) => synchronizationContext.Post(_ => OnGetTagsInBackgroundComplete(completeReason, channel, cancellationTokenSource), null),
                (processedLineRange, tagList) => synchronizationContext.Post(_ => OnGetTagsInBackgroundProgress(cancellationTokenSource, processedLineRange, tagList), null));

            // Create the Task which will handle the actual gathering of data.  If there is a delay
            // specified use it
            var    localDelay = _asyncTaggerSource.Delay;
            Action taskAction =
                () =>
            {
                if (localDelay.HasValue)
                {
                    Thread.Sleep(localDelay.Value);
                }

                getTags();
            };

            var task = new Task(taskAction, cancellationToken);

            _asyncBackgroundRequest = new AsyncBackgroundRequest(
                span.Snapshot,
                channel,
                task,
                cancellationTokenSource);

            task.Start();
        }