private List <ITagSpan <TextMarkerTag> > GetTags(SnapshotSpan span)
 {
     return(_asyncTaggerSource.GetTagsInBackground(
                _asyncTaggerSourceRaw.GetDataForSpan(),
                span,
                CancellationToken.None).ToList());
 }
Example #2
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);
        }