public void FulfillOutstandingRequests() { Create("cat", "dog", "fish", "tree"); _asyncTagger.ChunkCount = 1; _asyncTaggerSource.SetBackgroundFunc( span => { var lineRange = SnapshotLineRange.CreateForSpan(span); if (lineRange.LineRange.ContainsLineNumber(0)) { return(span.Snapshot.GetLineFromLineNumber(0).Extent); } if (lineRange.LineRange.ContainsLineNumber(1)) { return(span.Snapshot.GetLineFromLineNumber(1).Extent); } return(null); }); for (int i = 0; i < _textBuffer.CurrentSnapshot.LineCount; i++) { var span = _textBuffer.GetLineSpan(i, i); var col = new NormalizedSnapshotSpanCollection(span); _asyncTagger.GetTags(col); } _asyncTaggerSource.Delay = null; WaitForBackgroundToComplete(); var tags = _asyncTagger.GetTags(new NormalizedSnapshotSpanCollection(_textBuffer.GetExtent())); Assert.Equal(2, tags.Count()); }
private IEnumerable <ITagSpan <TTag> > GetTags(SnapshotSpan span) { var lineRange = SnapshotLineRange.CreateForSpan(span); EditorUtilsTrace.TraceInfo("AsyncTagger::GetTags {0} - {1}", lineRange.StartLineNumber, lineRange.LastLineNumber); // First try and see if the tagger can provide prompt data. We want to avoid // creating Task<T> instances if possible. IEnumerable <ITagSpan <TTag> > tagList = EmptyTagList; if (!TryGetTagsPrompt(span, out tagList) && !TryGetTagsFromBackgroundDataCache(span, out tagList)) { // The request couldn't be fully satisfied from the cache or prompt data so // we will request the data in the background thread GetTagsInBackground(span); // Since the request couldn't be fully fulfilled by the cache we augment the returned data // with our tracking data var trackingTagList = GetTagsFromTrackingDataCache(span.Snapshot); tagList = tagList == null ? trackingTagList : tagList.Concat(trackingTagList); } // Now filter the set of returned ITagSpan values to those which are part of the // requested NormalizedSnapshotSpanCollection. The cache lookups don't dig down and // instead return all available tags. We filter down the collection here to what's // necessary. return(tagList.Where(tagSpan => tagSpan.Span.IntersectsWith(span))); }
private void RaiseTagsChanged(SnapshotSpan span) { var lineRange = SnapshotLineRange.CreateForSpan(span); EditorUtilsTrace.TraceInfo("AsyncTagger::RaiseTagsChanged {0} - {1}", lineRange.StartLineNumber, lineRange.LastLineNumber); if (_tagsChanged != null) { _tagsChanged(this, new SnapshotSpanEventArgs(span)); } }
internal AsyncTaggerType.AsyncBackgroundRequest CreateAsyncBackgroundRequest( SnapshotSpan span, CancellationTokenSource cancellationTokenSource, Task task = null) { var channel = new AsyncTagger <string, TextMarkerTag> .Channel(); channel.WriteNormal(SnapshotLineRange.CreateForSpan(span)); task = task ?? new Task(() => { }); return(new AsyncTaggerType.AsyncBackgroundRequest( span.Snapshot, channel, task, cancellationTokenSource)); }
internal ReadOnlyCollection <ITagSpan <TextMarkerTag> > GetTags(SnapshotSpan span) { var lineRange = SnapshotLineRange.CreateForSpan(span); Debug.WriteLine("WordUnderCaret Version {0}, Lines {1} - {2}", span.Snapshot.Version.VersionNumber, lineRange.StartLineNumber, lineRange.LastLineNumber); var tags = new List <ITagSpan <TextMarkerTag> >(); foreach (var snapshotLine in lineRange.Lines) { AddWordsOnLine(tags, snapshotLine); _cancellationToken.ThrowIfCancellationRequested(); } return(tags.ToReadOnlyCollectionShallow()); }
protected override ReadOnlyCollection <ITagSpan <TextMarkerTag> > GetTagsInBackground(string data, SnapshotSpan span, CancellationToken cancellationToken) { var lineRange = SnapshotLineRange.CreateForSpan(span); var list = new List <ITagSpan <TextMarkerTag> >(); foreach (var snapshotLine in lineRange.Lines) { cancellationToken.ThrowIfCancellationRequested(); var commentSpan = MatchLine(snapshotLine); if (commentSpan.HasValue) { var tagSpan = new TagSpan <TextMarkerTag>(commentSpan.Value, s_tag); list.Add(tagSpan); } } return(list.ToReadOnlyCollectionShallow()); }
internal ReadOnlyCollection <ITagSpan <TextMarkerTag> > GetTags(SnapshotSpan span) { var tags = new List <ITagSpan <TextMarkerTag> >(); var lineRange = SnapshotLineRange.CreateForSpan(span); Debug.WriteLine("Cat Version {0}, Lines {1} - {2}", span.Snapshot.Version.VersionNumber, lineRange.StartLineNumber, lineRange.LastLineNumber); foreach (var snapshotLine in lineRange.Lines) { AddWordsOnLine(tags, snapshotLine); _cancellationToken.ThrowIfCancellationRequested(); } // Cats need naps Debug.WriteLine("Cat Nap Time"); Thread.Sleep(TimeSpan.FromSeconds(1)); Debug.WriteLine("Cat Wake Up"); return(tags.ToReadOnlyCollectionShallow()); }
/// <summary> /// Determine tag cache state we have for the given SnapshotSpan /// </summary> internal TagCacheState GetTagCacheState(SnapshotSpan span) { // If the requested span doesn't even intersect with the overarching SnapshotSpan // of the cached data in the background then a more exhaustive search isn't needed // at this time var cachedSpan = Span; if (!cachedSpan.IntersectsWith(span)) { return(TagCacheState.None); } var lineRange = SnapshotLineRange.CreateForSpan(span); var unvisited = VisitedCollection.GetUnvisited(lineRange.LineRange); return(unvisited.HasValue ? TagCacheState.Partial : TagCacheState.Complete); }
internal AsyncTaggerType.BackgroundCacheData CreateBackgroundCacheData(SnapshotSpan source, params SnapshotSpan[] tagSpans) { return(new AsyncTaggerType.BackgroundCacheData( SnapshotLineRange.CreateForSpan(source), tagSpans.Select(CreateTagSpan).ToReadOnlyCollection())); }
internal BackgroundCacheData <TextMarkerTag> CreateBackgroundCacheData(SnapshotSpan source, params SnapshotSpan[] tagSpans) { return(new BackgroundCacheData <TextMarkerTag>( SnapshotLineRange.CreateForSpan(source), tagSpans.Select(CreateTagSpan).ToReadOnlyCollection())); }
/// <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(); }