private void AsyncUpdate() { // Store the snapshot that we're now current with and send an event // for the text that has changed. if (Snapshot != TextView.TextBuffer.CurrentSnapshot) { Snapshot = TextView.TextBuffer.CurrentSnapshot; var translatedAdornmentCache = adormentDictionariesPool.Get(); Debug.Assert(translatedAdornmentCache.Count == 0); lock (adornmentsCache) { foreach (var keyValuePair in adornmentsCache) { var newKey = new AdornmentCacheKey(keyValuePair.Key.Span.TranslateTo(Snapshot, SpanTrackingMode.EdgeExclusive)); if (!translatedAdornmentCache.ContainsKey(newKey)) { translatedAdornmentCache.Add(newKey, keyValuePair.Value); } } adornmentsCache.Clear(); adormentDictionariesPool.Put(adornmentsCache); adornmentsCache = translatedAdornmentCache; } } int minSpan = int.MaxValue, maxSpan = int.MinValue; lock (invalidatedSpans) { if (invalidatedSpans.Count == 0) { return; } for (int i = 0; i < invalidatedSpans.Count; i++) { var translatedSpan = invalidatedSpans[i].TranslateTo(Snapshot, SpanTrackingMode.EdgeInclusive); if (translatedSpan.Start < minSpan) { minSpan = translatedSpan.Start; } if (translatedSpan.End > maxSpan) { maxSpan = translatedSpan.End; } } invalidatedSpans.Clear(); } RaiseTagsChanged(new SnapshotSpan(Snapshot, minSpan, maxSpan - minSpan)); }
private PooledStructEnumerable <ITagSpan <IntraTextAdornmentTag> > GetAdornmentTagsOnSnapshot(ITextSnapshot snapshot) { // Since WPF UI objects have state (like mouse hover or animation) and are relatively expensive to create and lay out, // this code tries to reuse controls as much as possible. // The controls are stored in this.adornmentCache between the calls. var results = tagSpanListsPool.Get(); Debug.Assert(results.Count == 0); var toRemove = adornmentCacheKeyHashsetsPool.Get(); Debug.Assert(toRemove.Count == 0); lock (adornmentsCache) { // Mark which adornments fall inside the requested spans with Keep=false // so that they can be removed from the cache if they no longer correspond to data tags. foreach (var kv in adornmentsCache) { toRemove.Add(kv.Key); } foreach (var tagData in GetAdornmentData(new NormalizedSnapshotSpanCollection(snapshot, new Span(0, snapshot.Length)))) { // Look up the corresponding adornment or create one if it's new. AdornmentInfo adornmentInfo; var key = new AdornmentCacheKey(tagData.Span); if (adornmentsCache.TryGetValue(key, out TAdornment adornment)) { adornmentInfo = tagData.GetAdornmentInfo(adornment.DisplayMode); UpdateAdornment(adornment, tagData.Tag, snapshot); toRemove.Remove(key); Debug.WriteLine($"Updating adornment {adornment.Index}"); } else { adornmentInfo = tagData.GetAdornmentInfo(Mode); if (adornmentsPool.Count > 0) { adornment = adornmentsPool[adornmentsPool.Count - 1]; adornmentsPool.RemoveAt(adornmentsPool.Count - 1); UpdateAdornment(adornment, tagData.Tag, snapshot); adornment.CurrentState = TeXCommentAdornmentState.EditingAndRenderingPreview; Debug.WriteLine($"Reusing adornment {adornment.Index} from pool"); } else { adornment = CreateAdornment(tagData.Tag, snapshot); adornment.DisplayModeChanged += (s, e) => perSnapshotResults.UpdateValue(Snapshot); //will be deattached on dispose Debug.WriteLine($"Creating adornment {adornment.Index}"); } if (adornment == null) { continue; } // Get the adornment to measure itself. Its DesiredSize property is used to determine // how much space to leave between text for this adornment. // Note: If the size of the adornment changes, the line will be reformatted to accommodate it. // Note: Some adornments may change size when added to the view's visual tree due to inherited // dependency properties that affect layout. Such options can include SnapsToDevicePixels, // UseLayoutRounding, TextRenderingMode, TextHintingMode, and TextFormattingMode. Making sure // that these properties on the adornment match the view's values before calling Measure here // can help avoid the size change and the resulting unnecessary re-format. adornment.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); adornmentsCache.Add(key, adornment); } Debug.WriteLine($"Yielding adornment {adornment.Index} with span {adornmentInfo.Span}"); results.Add(new TagSpan <IntraTextAdornmentTag>(adornmentInfo.Span, new IntraTextAdornmentTag(adornment, null, adornmentInfo.Affinity))); } foreach (var adornmentKey in toRemove) { if (adornmentsPool.Count < MaxAdornmentPoolSize) { adornmentsPool.Add(adornmentsCache[adornmentKey]); } adornmentsCache.Remove(adornmentKey); } toRemove.Clear(); adornmentCacheKeyHashsetsPool.Put(toRemove); } return(new PooledStructEnumerable <ITagSpan <IntraTextAdornmentTag> >(results, tagSpanListsPool)); }